notionary 0.1.29__py3-none-any.whl → 0.2.0__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 +5 -5
- notionary/database/notion_database.py +50 -59
- notionary/database/notion_database_factory.py +16 -20
- notionary/elements/audio_element.py +1 -1
- notionary/elements/bookmark_element.py +1 -1
- notionary/elements/bulleted_list_element.py +2 -8
- notionary/elements/callout_element.py +1 -1
- notionary/elements/code_block_element.py +1 -1
- notionary/elements/divider_element.py +1 -1
- notionary/elements/embed_element.py +1 -1
- notionary/elements/heading_element.py +2 -8
- notionary/elements/image_element.py +1 -1
- notionary/elements/mention_element.py +1 -1
- notionary/elements/notion_block_element.py +1 -1
- notionary/elements/numbered_list_element.py +2 -7
- notionary/elements/paragraph_element.py +1 -1
- notionary/elements/qoute_element.py +1 -1
- notionary/elements/registry/{block_element_registry.py → block_registry.py} +70 -26
- notionary/elements/registry/{block_element_registry_builder.py → block_registry_builder.py} +48 -32
- notionary/elements/table_element.py +1 -1
- notionary/elements/text_inline_formatter.py +13 -9
- notionary/elements/todo_element.py +1 -1
- notionary/elements/toggle_element.py +1 -1
- notionary/elements/toggleable_heading_element.py +1 -1
- notionary/elements/video_element.py +1 -1
- notionary/models/notion_block_response.py +264 -0
- notionary/models/notion_database_response.py +63 -0
- notionary/models/notion_page_response.py +100 -0
- notionary/notion_client.py +38 -5
- notionary/page/content/page_content_retriever.py +68 -0
- notionary/page/content/page_content_writer.py +103 -0
- notionary/page/markdown_to_notion_converter.py +5 -5
- notionary/page/metadata/metadata_editor.py +91 -63
- notionary/page/metadata/notion_icon_manager.py +55 -28
- notionary/page/metadata/notion_page_cover_manager.py +23 -20
- notionary/page/notion_page.py +223 -218
- notionary/page/notion_page_factory.py +102 -151
- notionary/page/notion_to_markdown_converter.py +5 -5
- notionary/page/properites/database_property_service.py +11 -55
- notionary/page/properites/page_property_manager.py +44 -67
- notionary/page/properites/property_value_extractor.py +3 -3
- notionary/page/relations/notion_page_relation_manager.py +165 -213
- notionary/page/relations/notion_page_title_resolver.py +59 -41
- notionary/page/relations/page_database_relation.py +7 -9
- notionary/{elements/prompts → prompting}/element_prompt_content.py +19 -4
- notionary/prompting/markdown_syntax_prompt_generator.py +92 -0
- notionary/util/logging_mixin.py +17 -8
- notionary/util/warn_direct_constructor_usage.py +54 -0
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/METADATA +2 -1
- notionary-0.2.0.dist-info/RECORD +60 -0
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/WHEEL +1 -1
- notionary/database/database_info_service.py +0 -43
- notionary/elements/prompts/synthax_prompt_builder.py +0 -150
- notionary/page/content/page_content_manager.py +0 -211
- notionary/page/properites/property_operation_result.py +0 -116
- notionary/page/relations/relation_operation_result.py +0 -144
- notionary-0.1.29.dist-info/RECORD +0 -58
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/top_level.txt +0 -0
@@ -24,14 +24,19 @@ class ElementPromptContent:
|
|
24
24
|
avoid: Optional[str] = None
|
25
25
|
"""Optional field listing scenarios when this element should be avoided."""
|
26
26
|
|
27
|
+
is_standard_markdown: bool = False
|
28
|
+
"""Indicates whether this element follows standard Markdown syntax (and does not require full examples)."""
|
29
|
+
|
27
30
|
def __post_init__(self):
|
28
31
|
"""Validates that the content meets minimum requirements."""
|
29
32
|
if not self.description:
|
30
33
|
raise ValueError("Description is required")
|
31
34
|
if not self.syntax:
|
32
35
|
raise ValueError("Syntax is required")
|
33
|
-
if not self.examples:
|
34
|
-
raise ValueError(
|
36
|
+
if not self.examples and not self.is_standard_markdown:
|
37
|
+
raise ValueError(
|
38
|
+
"At least one example is required unless it's standard markdown."
|
39
|
+
)
|
35
40
|
if not self.when_to_use:
|
36
41
|
raise ValueError("Usage guidelines are required")
|
37
42
|
|
@@ -48,6 +53,7 @@ class ElementPromptBuilder:
|
|
48
53
|
self._examples: List[str] = []
|
49
54
|
self._when_to_use: Optional[str] = None
|
50
55
|
self._avoid: Optional[str] = None
|
56
|
+
self._is_standard_markdown = False
|
51
57
|
|
52
58
|
def with_description(self, description: str) -> Self:
|
53
59
|
"""Set the description of the element."""
|
@@ -79,6 +85,12 @@ class ElementPromptBuilder:
|
|
79
85
|
self._avoid = avoid
|
80
86
|
return self
|
81
87
|
|
88
|
+
def with_standard_markdown(self) -> Self:
|
89
|
+
"""Indicate that this element follows standard Markdown syntax."""
|
90
|
+
self._examples = []
|
91
|
+
self._is_standard_markdown = True
|
92
|
+
return self
|
93
|
+
|
82
94
|
def build(self) -> ElementPromptContent:
|
83
95
|
"""
|
84
96
|
Build and validate the ElementPromptContent object.
|
@@ -93,8 +105,10 @@ class ElementPromptBuilder:
|
|
93
105
|
raise ValueError("Description is required")
|
94
106
|
if not self._syntax:
|
95
107
|
raise ValueError("Syntax is required")
|
96
|
-
if not self._examples:
|
97
|
-
raise ValueError(
|
108
|
+
if not self._examples and not self._is_standard_markdown:
|
109
|
+
raise ValueError(
|
110
|
+
"At least one example is required unless it's standard markdown."
|
111
|
+
)
|
98
112
|
if not self._when_to_use:
|
99
113
|
raise ValueError("Usage guidelines are required")
|
100
114
|
|
@@ -104,4 +118,5 @@ class ElementPromptBuilder:
|
|
104
118
|
examples=self._examples,
|
105
119
|
when_to_use=self._when_to_use,
|
106
120
|
avoid=self._avoid,
|
121
|
+
is_standard_markdown=self._is_standard_markdown,
|
107
122
|
)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Type, List
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
|
6
|
+
|
7
|
+
class MarkdownSyntaxPromptGenerator:
|
8
|
+
"""
|
9
|
+
Generator for LLM system prompts that describe Notion-Markdown syntax.
|
10
|
+
|
11
|
+
This class extracts information about supported Markdown patterns
|
12
|
+
and formats them optimally for LLMs.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(self):
|
16
|
+
# Lade das Template aus der Markdown-Datei
|
17
|
+
self.SYSTEM_PROMPT_TEMPLATE = self._load_template()
|
18
|
+
|
19
|
+
def _load_template(self) -> str:
|
20
|
+
"""
|
21
|
+
Lädt das Prompt-Template aus der Markdown-Datei.
|
22
|
+
"""
|
23
|
+
current_dir = Path(__file__).parent
|
24
|
+
template_path = current_dir / "res/notion_syntax_prompt.md"
|
25
|
+
|
26
|
+
try:
|
27
|
+
with open(template_path, "r", encoding="utf-8") as file:
|
28
|
+
return file.read()
|
29
|
+
except FileNotFoundError:
|
30
|
+
raise FileNotFoundError(f"Template file not found at {template_path}")
|
31
|
+
except Exception as e:
|
32
|
+
raise RuntimeError(f"Error loading template file: {e}")
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def generate_element_doc(element_class: Type[NotionBlockElement]) -> str:
|
36
|
+
"""
|
37
|
+
Generates documentation for a specific NotionBlockElement in a compact format.
|
38
|
+
Uses the element's get_llm_prompt_content method if available.
|
39
|
+
"""
|
40
|
+
class_name = element_class.__name__
|
41
|
+
element_name = class_name.replace("Element", "")
|
42
|
+
|
43
|
+
content = element_class.get_llm_prompt_content()
|
44
|
+
|
45
|
+
doc_parts = [
|
46
|
+
f"## {element_name}",
|
47
|
+
f"{content.description}",
|
48
|
+
f"**Syntax:** {content.syntax}",
|
49
|
+
]
|
50
|
+
|
51
|
+
if content.examples:
|
52
|
+
doc_parts.append("**Examples:**")
|
53
|
+
for example in content.examples:
|
54
|
+
doc_parts.append(example)
|
55
|
+
|
56
|
+
doc_parts.append(f"**When to use:** {content.when_to_use}")
|
57
|
+
|
58
|
+
if content.avoid:
|
59
|
+
doc_parts.append(f"**Avoid:** {content.avoid}")
|
60
|
+
|
61
|
+
return "\n".join([part for part in doc_parts if part])
|
62
|
+
|
63
|
+
@classmethod
|
64
|
+
def generate_element_docs(
|
65
|
+
cls, element_classes: List[Type[NotionBlockElement]]
|
66
|
+
) -> str:
|
67
|
+
"""
|
68
|
+
Generates complete documentation for all provided element classes.
|
69
|
+
"""
|
70
|
+
docs = [
|
71
|
+
"# Markdown Syntax for Notion Blocks",
|
72
|
+
"The following Markdown patterns are supported for creating Notion blocks:",
|
73
|
+
]
|
74
|
+
|
75
|
+
# Generate docs for each element
|
76
|
+
for element in element_classes:
|
77
|
+
docs.append("\n" + cls.generate_element_doc(element))
|
78
|
+
|
79
|
+
return "\n".join(docs)
|
80
|
+
|
81
|
+
@classmethod
|
82
|
+
def generate_system_prompt(
|
83
|
+
cls,
|
84
|
+
element_classes: List[Type[NotionBlockElement]],
|
85
|
+
) -> str:
|
86
|
+
"""
|
87
|
+
Generates a complete system prompt for LLMs.
|
88
|
+
"""
|
89
|
+
# Erstelle eine Instanz, um das Template zu laden
|
90
|
+
instance = cls()
|
91
|
+
element_docs = cls.generate_element_docs(element_classes)
|
92
|
+
return instance.SYSTEM_PROMPT_TEMPLATE.format(element_docs=element_docs)
|
notionary/util/logging_mixin.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import inspect
|
3
|
+
from typing import Optional, ClassVar
|
3
4
|
|
4
5
|
|
5
6
|
def setup_logging():
|
@@ -10,19 +11,27 @@ def setup_logging():
|
|
10
11
|
|
11
12
|
|
12
13
|
class LoggingMixin:
|
14
|
+
# Class attribute with proper typing
|
15
|
+
logger: ClassVar[logging.Logger] = None
|
16
|
+
|
17
|
+
def __init_subclass__(cls, **kwargs):
|
18
|
+
"""
|
19
|
+
This method is called when a class inherits from LoggingMixin.
|
20
|
+
It automatically sets up the logger as a class attribute.
|
21
|
+
"""
|
22
|
+
super().__init_subclass__(**kwargs)
|
23
|
+
cls.logger = logging.getLogger(cls.__name__)
|
24
|
+
|
13
25
|
@property
|
14
|
-
def
|
26
|
+
def instance_logger(self) -> logging.Logger:
|
27
|
+
"""Instance logger - for instance methods"""
|
15
28
|
if not hasattr(self, "_logger"):
|
16
29
|
self._logger = logging.getLogger(self.__class__.__name__)
|
17
30
|
return self._logger
|
18
31
|
|
19
|
-
@classmethod
|
20
|
-
def class_logger(cls):
|
21
|
-
"""Class logger - für Klassenmethoden"""
|
22
|
-
return logging.getLogger(cls.__name__)
|
23
|
-
|
24
32
|
@staticmethod
|
25
|
-
def static_logger():
|
33
|
+
def static_logger() -> logging.Logger:
|
34
|
+
"""Static logger - for static methods"""
|
26
35
|
stack = inspect.stack()
|
27
36
|
for frame_info in stack[1:]:
|
28
37
|
class_name = LoggingMixin._get_class_name_from_frame(frame_info.frame)
|
@@ -31,7 +40,7 @@ class LoggingMixin:
|
|
31
40
|
return logging.getLogger("UnknownStaticContext")
|
32
41
|
|
33
42
|
@staticmethod
|
34
|
-
def _get_class_name_from_frame(frame):
|
43
|
+
def _get_class_name_from_frame(frame) -> Optional[str]:
|
35
44
|
local_vars = frame.f_locals
|
36
45
|
if "self" in local_vars:
|
37
46
|
return local_vars["self"].__class__.__name__
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import functools
|
2
|
+
import inspect
|
3
|
+
from typing import Callable, Any, TypeVar, cast
|
4
|
+
|
5
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
6
|
+
|
7
|
+
|
8
|
+
def warn_direct_constructor_usage(func: F) -> F:
|
9
|
+
"""
|
10
|
+
Method decorator that logs a warning when the constructor is called directly
|
11
|
+
instead of through a factory method.
|
12
|
+
|
13
|
+
This is an advisory decorator - it only logs a warning and doesn't
|
14
|
+
prevent direct constructor usage.
|
15
|
+
"""
|
16
|
+
|
17
|
+
@functools.wraps(func)
|
18
|
+
def wrapper(self, *args, **kwargs):
|
19
|
+
# Get the call stack
|
20
|
+
stack = inspect.stack()
|
21
|
+
|
22
|
+
self._from_factory = False
|
23
|
+
|
24
|
+
search_depth = min(6, len(stack))
|
25
|
+
|
26
|
+
for i in range(1, search_depth):
|
27
|
+
if i >= len(stack):
|
28
|
+
break
|
29
|
+
|
30
|
+
caller_frame = stack[i]
|
31
|
+
caller_name = caller_frame.function
|
32
|
+
|
33
|
+
# Debug logging might be helpful during development
|
34
|
+
# print(f"Frame {i}: {caller_name}")
|
35
|
+
|
36
|
+
# If called from a factory method, mark it and break
|
37
|
+
if caller_name.startswith("create_from_") or caller_name.startswith(
|
38
|
+
"from_"
|
39
|
+
):
|
40
|
+
self._from_factory = True
|
41
|
+
break
|
42
|
+
|
43
|
+
# If not from factory, log warning
|
44
|
+
if not self._from_factory and hasattr(self, "logger"):
|
45
|
+
self.logger.warning(
|
46
|
+
"Advisory: Direct constructor usage is discouraged. "
|
47
|
+
"Consider using factory methods like create_from_page_id(), "
|
48
|
+
"create_from_url(), or create_from_page_name() instead."
|
49
|
+
)
|
50
|
+
|
51
|
+
# Call the original __init__
|
52
|
+
return func(self, *args, **kwargs)
|
53
|
+
|
54
|
+
return cast(F, wrapper)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: notionary
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: A toolkit to convert between Markdown and Notion blocks
|
5
5
|
Home-page: https://github.com/mathisarends/notionary
|
6
6
|
Author: Mathis Arends
|
@@ -12,6 +12,7 @@ Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
13
13
|
Requires-Dist: httpx>=0.28.0
|
14
14
|
Requires-Dist: python-dotenv>=1.1.0
|
15
|
+
Requires-Dist: pydantic>=2.11.4
|
15
16
|
Dynamic: author
|
16
17
|
Dynamic: author-email
|
17
18
|
Dynamic: classifier
|
@@ -0,0 +1,60 @@
|
|
1
|
+
notionary/__init__.py,sha256=hPvZ-iqt5R_dAs9KaRBhC5eXzuQ5uvt-9EaU2O_7bZw,691
|
2
|
+
notionary/notion_client.py,sha256=sJJMB36DqL0abcG-5w_plUDeS-zn1x0LpCgVNYeKqx0,7413
|
3
|
+
notionary/database/database_discovery.py,sha256=qDGFhXG9s-_6CXdRg8tMiwX4dvX7jLjgAUFPSNlYtlI,4506
|
4
|
+
notionary/database/notion_database.py,sha256=zbHPejETr101pprd7kewZ555d_TONN_wJi7b9Eyfoyg,7634
|
5
|
+
notionary/database/notion_database_factory.py,sha256=FmijGYz6A4mCWVionOg9sxgFXfb9he52xdgNswJw24k,6584
|
6
|
+
notionary/database/models/page_result.py,sha256=Vmm5_oYpYAkIIJVoTd1ZZGloeC3cmFLMYP255mAmtaw,233
|
7
|
+
notionary/elements/audio_element.py,sha256=7bEpFl9jA6S1UZlEXsmFzEUVoViEp1o_7zZIC-S7750,5345
|
8
|
+
notionary/elements/bookmark_element.py,sha256=msCtZvuPkIj1kiShNwE8i1GDYwamFb5mwRyZm4XyVY4,8145
|
9
|
+
notionary/elements/bulleted_list_element.py,sha256=obsb3JqUNET3uS5OZM3yzDqxSzJzUuEob-Fzx0UIg9Y,2664
|
10
|
+
notionary/elements/callout_element.py,sha256=ZsRvRtVy9kxdTwgrB5JGjZ4qcCiwcC0WimWJ_cW0aLY,4492
|
11
|
+
notionary/elements/code_block_element.py,sha256=IbwpptMLtHDFO0Hyvt2o0p5AZ0S4vhxfzoBhTqKexSY,6240
|
12
|
+
notionary/elements/divider_element.py,sha256=0e10YK-CC8uGuL7921dEIjeJK9ha-WhRIYRf2fFuxVQ,2211
|
13
|
+
notionary/elements/embed_element.py,sha256=Zcc18Kl8SGoG98P2aYE0TkBviRvSz-sYOdjMEs-tvgk,4579
|
14
|
+
notionary/elements/heading_element.py,sha256=kqgjyfaawEODir2tzDyf7-7wm38DbqoZnsH5k94GsA0,3013
|
15
|
+
notionary/elements/image_element.py,sha256=cwdovaWK8e4uZJU97l_fJ2etAxAgM2rG2EE34t4eag8,4758
|
16
|
+
notionary/elements/mention_element.py,sha256=L4t6eAY3RcbOqIiwVT_CAqwatDtP4tBs9FaqRhaCbpQ,8227
|
17
|
+
notionary/elements/notion_block_element.py,sha256=BVrZH09vyojuacs3KGReVx3W0Ee6di_5o9E8N5sex28,1258
|
18
|
+
notionary/elements/numbered_list_element.py,sha256=LHZ3aQjz8mHQKOd_oGgbaaj5Hv9_ZQVomj3GaTP8r1E,2663
|
19
|
+
notionary/elements/paragraph_element.py,sha256=RfnC-whzmE2eysbTtTNsswmWBqxqK0lUdDlinHKsFMg,3255
|
20
|
+
notionary/elements/qoute_element.py,sha256=NsMus2tiAKr8e2HBnHAZ442w40_qxL96z0-BzwR0uYU,6122
|
21
|
+
notionary/elements/table_element.py,sha256=5ghOVjo5ocEGaQPPzbdbzcF8TQ3kLRJ2AYdsA2uHDJk,11249
|
22
|
+
notionary/elements/text_inline_formatter.py,sha256=KvvTqctFNlzBo-OMoShAMnu-oK_AeiKYqslYQ-2dUFY,7963
|
23
|
+
notionary/elements/todo_element.py,sha256=ND3oOzSnd0l1AUGTcG2NiHW50ZbI4-atjtNorLV5m2U,4124
|
24
|
+
notionary/elements/toggle_element.py,sha256=h9vYkkAIUHzn-0mu31qC6UPdlk_0EFIsU5A4T_A2ZI8,11082
|
25
|
+
notionary/elements/toggleable_heading_element.py,sha256=XdaPsd8anufwAACL8J-Egd_RcqPqZ1gFlzeol1GOyyc,9960
|
26
|
+
notionary/elements/video_element.py,sha256=y0OmOYXdQBc2rSYAHRmA4l4rzNqPnyhuXbEipcgzQgY,5727
|
27
|
+
notionary/elements/registry/block_registry.py,sha256=giWGcdgc3Z60wvfUr-FS6UMc-k-Q6DlXO8T0gl4fVC8,5027
|
28
|
+
notionary/elements/registry/block_registry_builder.py,sha256=5dQhWiJ7jsyKUin1y7r-1Cmp0oOEAIfh6g91w8O4ydI,9319
|
29
|
+
notionary/exceptions/database_exceptions.py,sha256=I-Tx6bYRLpi5pjGPtbT-Mqxvz3BFgYTiuZxknJeLxtI,2638
|
30
|
+
notionary/exceptions/page_creation_exception.py,sha256=4v7IuZD6GsQLrqhDLriGjuG3ML638gAO53zDCrLePuU,281
|
31
|
+
notionary/models/notion_block_response.py,sha256=gzL4C6K9QPcaMS6NbAZaRceSEnMbNwYBVVzxysza5VU,6002
|
32
|
+
notionary/models/notion_database_response.py,sha256=Ij8XZniAi2BGjKn2fzT7auCAYAnTzL-jPTUjj5uH7i0,1240
|
33
|
+
notionary/models/notion_page_response.py,sha256=r4fwMwwDocj92JdbSmyrzIqBKsnEaz4aDUiPabrg9BM,1762
|
34
|
+
notionary/page/markdown_to_notion_converter.py,sha256=EuqUGNv2HZu67INOnGheeJkt7WHTWGuLnhEG72_Wv5Y,15833
|
35
|
+
notionary/page/notion_page.py,sha256=Ap5h-6Ef9K8gtgrMM-msaYRM4q7OgFvzZS_xSjLdBiU,18049
|
36
|
+
notionary/page/notion_page_factory.py,sha256=2A3M5Ub_kV2-q7PPRqDgfwBjhkGCwtL5i3Kr2RfvvVo,7213
|
37
|
+
notionary/page/notion_to_markdown_converter.py,sha256=vUQss0J7LUFLULGvW27PjaTFuWi8OsRQAUBowSYorkM,6408
|
38
|
+
notionary/page/content/notion_page_content_chunker.py,sha256=xRks74Dqec-De6-AVTxMPnXs-MSJBzSm1HfJfaHiKr8,3330
|
39
|
+
notionary/page/content/page_content_retriever.py,sha256=btVWarx06KZ2A2ZRxNpNEvkeYwyyI2tnM8dKWSkiQGQ,2235
|
40
|
+
notionary/page/content/page_content_writer.py,sha256=czBzNCGcwdpqNLSQPyna1s8Y7pjyPzDgJC3UUK5PLGA,3793
|
41
|
+
notionary/page/metadata/metadata_editor.py,sha256=HI7m8Zn_Lz6x36rBnW1EnbicVS-4Q8NmCJYKN-OlY-c,5130
|
42
|
+
notionary/page/metadata/notion_icon_manager.py,sha256=6a9GS5sT0trfuAb0hlF2Cw_Wc1oM59a1QA4kO9asvMA,2576
|
43
|
+
notionary/page/metadata/notion_page_cover_manager.py,sha256=gHQSA8EtO4gbkMt_C3nKc0DF44SY_4ycd57cJSihdqk,2215
|
44
|
+
notionary/page/properites/database_property_service.py,sha256=-UlA3NlquQAVyDy4Eyshy9J70UpIvv7K_PBPgvxVdCo,9909
|
45
|
+
notionary/page/properites/page_property_manager.py,sha256=UVVcSwm3C9y-ggBdaca0o3Ib16YCm6do2ZDKLk8kdBA,5743
|
46
|
+
notionary/page/properites/property_formatter.py,sha256=d_Nr5XQxgjB6VIS0u3ey14MOUKY416o_BvdXjbkUNAQ,3667
|
47
|
+
notionary/page/properites/property_value_extractor.py,sha256=TZIbJXWcA1UQ7FutbGlJ96YoTdJp4I_Mz5qlTMgGFuU,2356
|
48
|
+
notionary/page/relations/notion_page_relation_manager.py,sha256=tfkvLHClaYel_uEad1PIZ7yzhb2tXS-QrLn1CBvUuuw,11069
|
49
|
+
notionary/page/relations/notion_page_title_resolver.py,sha256=dIjiEeHjjNT-DrIhz1nynkfHkMpUuJJFOEjb25Wy7f4,3575
|
50
|
+
notionary/page/relations/page_database_relation.py,sha256=8lEp8fQjPwjWhA8nZu3k8mW6EEc54ki1Uwf4iUV1DOU,2245
|
51
|
+
notionary/prompting/element_prompt_content.py,sha256=tHref-SKA81Ua_IQD2Km7y7BvFtHl74haSIjHNYE3FE,4403
|
52
|
+
notionary/prompting/markdown_syntax_prompt_generator.py,sha256=xKzTF62SFKzadyC7FHcOxWueRzkKiJ054pBHu9B4aLg,3155
|
53
|
+
notionary/util/logging_mixin.py,sha256=b6wHj0IoVSWXbHh0yynfJlwvIR33G2qmaGNzrqyb7Gs,1825
|
54
|
+
notionary/util/page_id_utils.py,sha256=EYNMxgf-7ghzL5K8lKZBZfW7g5CsdY0Xuj4IYmU8RPk,1381
|
55
|
+
notionary/util/warn_direct_constructor_usage.py,sha256=vyJR73F95XVSRWIbyij-82IGOpAne9SBPM25eDpZfSU,1715
|
56
|
+
notionary-0.2.0.dist-info/licenses/LICENSE,sha256=zOm3cRT1qD49eg7vgw95MI79rpUAZa1kRBFwL2FkAr8,1120
|
57
|
+
notionary-0.2.0.dist-info/METADATA,sha256=LJEmoURn20Z9L2j8sK3GartZp5tQHhdQpt5tZ4dVSWA,8374
|
58
|
+
notionary-0.2.0.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
59
|
+
notionary-0.2.0.dist-info/top_level.txt,sha256=fhONa6BMHQXqthx5PanWGbPL0b8rdFqhrJKVLf_adSs,10
|
60
|
+
notionary-0.2.0.dist-info/RECORD,,
|
@@ -1,43 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
from notionary.notion_client import NotionClient
|
3
|
-
|
4
|
-
|
5
|
-
class DatabaseInfoService:
|
6
|
-
"""Service für den Zugriff auf Datenbankinformationen"""
|
7
|
-
|
8
|
-
def __init__(self, client: NotionClient, database_id: str):
|
9
|
-
self._client = client
|
10
|
-
self.database_id = database_id
|
11
|
-
self._title = None
|
12
|
-
|
13
|
-
async def fetch_database_title(self) -> str:
|
14
|
-
"""
|
15
|
-
Fetch the database title from the Notion API.
|
16
|
-
|
17
|
-
Returns:
|
18
|
-
The database title or "Untitled" if no title is found
|
19
|
-
"""
|
20
|
-
db_details = await self._client.get(f"databases/{self.database_id}")
|
21
|
-
if not db_details:
|
22
|
-
return "Untitled"
|
23
|
-
|
24
|
-
title = "Untitled"
|
25
|
-
if "title" in db_details:
|
26
|
-
title_parts = []
|
27
|
-
for text_obj in db_details["title"]:
|
28
|
-
if "plain_text" in text_obj:
|
29
|
-
title_parts.append(text_obj["plain_text"])
|
30
|
-
|
31
|
-
if title_parts:
|
32
|
-
title = "".join(title_parts)
|
33
|
-
|
34
|
-
return title
|
35
|
-
|
36
|
-
@property
|
37
|
-
def title(self) -> Optional[str]:
|
38
|
-
return self._title
|
39
|
-
|
40
|
-
async def load_title(self) -> str:
|
41
|
-
"""Lädt den Titel der Datenbank und speichert ihn im Cache"""
|
42
|
-
self._title = await self.fetch_database_title()
|
43
|
-
return self._title
|
@@ -1,150 +0,0 @@
|
|
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
|
-
# Understanding Notion Blocks
|
17
|
-
Notion documents are composed of individual blocks. Each block has a specific type (paragraph, heading, list item, etc.) and format.
|
18
|
-
The Markdown syntax you use directly maps to these Notion blocks.
|
19
|
-
|
20
|
-
## Inline Formatting
|
21
|
-
Inline formatting can be used within most block types to style your text. You can combine multiple formatting options.
|
22
|
-
**Syntax:** **bold**, *italic*, `code`, ~~strikethrough~~, __underline__, [text](url)
|
23
|
-
**Examples:**
|
24
|
-
- This text has a **bold** word.
|
25
|
-
- This text has an *italic* word.
|
26
|
-
- This text has `code` formatting.
|
27
|
-
- This text has ~~strikethrough~~ formatting.
|
28
|
-
- This text has __underlined__ formatting.
|
29
|
-
- This has a [hyperlink](https://example.com).
|
30
|
-
- You can **combine *different* formatting** styles.
|
31
|
-
|
32
|
-
**When to use:** Use inline formatting to highlight important words, provide emphasis, show code or paths, or add hyperlinks. This helps create visual hierarchy and improves scanability.
|
33
|
-
|
34
|
-
## Spacers and Block Separation
|
35
|
-
There are two ways to create visual separation between blocks:
|
36
|
-
|
37
|
-
1. **Empty Lines**: Simply add a blank line between blocks
|
38
|
-
**Syntax:** Press Enter twice between blocks
|
39
|
-
**Example:**
|
40
|
-
First paragraph.
|
41
|
-
|
42
|
-
Second paragraph after an empty line.
|
43
|
-
|
44
|
-
2. **HTML Comment Spacer**: For more deliberate spacing between logical sections
|
45
|
-
**Syntax:** <!-- spacer -->
|
46
|
-
**Example:**
|
47
|
-
## First Section
|
48
|
-
Content here.
|
49
|
-
<!-- spacer -->
|
50
|
-
## Second Section
|
51
|
-
More content here.
|
52
|
-
|
53
|
-
**When to use:** Use empty lines for basic separation between blocks. Use the HTML comment spacer (<!-- spacer -->) to create more obvious visual separation between major logical sections of your document.
|
54
|
-
|
55
|
-
{element_docs}
|
56
|
-
|
57
|
-
CRITICAL USAGE GUIDELINES:
|
58
|
-
|
59
|
-
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.
|
60
|
-
|
61
|
-
2. INLINE FORMATTING - VERY IMPORTANT:
|
62
|
-
✅ You can use inline formatting within almost any block type.
|
63
|
-
✅ Combine **bold**, *italic*, `code`, and other formatting as needed.
|
64
|
-
✅ Format text to create visual hierarchy and emphasize important points.
|
65
|
-
❌ DO NOT overuse formatting - be strategic with formatting for best readability.
|
66
|
-
|
67
|
-
3. BACKTICK HANDLING - EXTREMELY IMPORTANT:
|
68
|
-
❌ NEVER wrap entire content or responses in triple backticks (```).
|
69
|
-
❌ DO NOT use triple backticks (```) for anything except CODE BLOCKS or DIAGRAMS.
|
70
|
-
❌ DO NOT use triple backticks to mark or highlight regular text or examples.
|
71
|
-
✅ USE triple backticks ONLY for actual programming code, pseudocode, or specialized notation.
|
72
|
-
✅ For inline code, use single backticks (`code`).
|
73
|
-
✅ When showing Markdown syntax examples, use inline code formatting with single backticks.
|
74
|
-
|
75
|
-
4. BLOCK SEPARATION - IMPORTANT:
|
76
|
-
✅ Use empty lines between different blocks to ensure proper rendering in Notion.
|
77
|
-
✅ For major logical sections, add the HTML comment spacer: <!-- spacer -->
|
78
|
-
✅ This spacer creates better visual breaks between key sections of your document.
|
79
|
-
⚠️ While headings can sometimes work without an empty line before the following paragraph, including empty lines between all block types ensures consistent rendering.
|
80
|
-
|
81
|
-
5. TOGGLE BLOCKS - NOTE:
|
82
|
-
✅ For toggle blocks and collapsible headings, use pipe prefixes (|) for content.
|
83
|
-
✅ Each line within a toggle should start with a pipe character followed by a space.
|
84
|
-
❌ Do not use the pipe character for any other blocks.
|
85
|
-
|
86
|
-
6. CONTENT FORMATTING - CRITICAL:
|
87
|
-
❌ DO NOT include introductory phrases like "I understand that..." or "Here's the content...".
|
88
|
-
✅ Provide ONLY the requested content directly without any prefacing text or meta-commentary.
|
89
|
-
✅ Generate just the content itself, formatted according to these guidelines.
|
90
|
-
"""
|
91
|
-
|
92
|
-
@staticmethod
|
93
|
-
def generate_element_doc(element_class: Type[NotionBlockElement]) -> str:
|
94
|
-
"""
|
95
|
-
Generates documentation for a specific NotionBlockElement in a compact format.
|
96
|
-
Uses the element's get_llm_prompt_content method if available.
|
97
|
-
"""
|
98
|
-
class_name = element_class.__name__
|
99
|
-
element_name = class_name.replace("Element", "")
|
100
|
-
|
101
|
-
# Check if the class has the get_llm_prompt_content method
|
102
|
-
if not hasattr(element_class, "get_llm_prompt_content") or not callable(
|
103
|
-
getattr(element_class, "get_llm_prompt_content")
|
104
|
-
):
|
105
|
-
return f"## {element_name}"
|
106
|
-
|
107
|
-
# Get the element content
|
108
|
-
content = element_class.get_llm_prompt_content()
|
109
|
-
|
110
|
-
doc_parts = [
|
111
|
-
f"## {element_name}",
|
112
|
-
f"{content.description}",
|
113
|
-
f"**Syntax:** {content.syntax}",
|
114
|
-
f"**Example:** {content.examples[0]}" if content.examples else "",
|
115
|
-
f"**When to use:** {content.when_to_use}",
|
116
|
-
]
|
117
|
-
|
118
|
-
if content.avoid:
|
119
|
-
doc_parts.append(f"**Avoid:** {content.avoid}")
|
120
|
-
|
121
|
-
return "\n".join([part for part in doc_parts if part])
|
122
|
-
|
123
|
-
@classmethod
|
124
|
-
def generate_element_docs(
|
125
|
-
cls, element_classes: List[Type[NotionBlockElement]]
|
126
|
-
) -> str:
|
127
|
-
"""
|
128
|
-
Generates complete documentation for all provided element classes.
|
129
|
-
"""
|
130
|
-
docs = [
|
131
|
-
"# Markdown Syntax for Notion Blocks",
|
132
|
-
"The following Markdown patterns are supported for creating Notion blocks:",
|
133
|
-
]
|
134
|
-
|
135
|
-
# Generate docs for each element
|
136
|
-
for element in element_classes:
|
137
|
-
docs.append("\n" + cls.generate_element_doc(element))
|
138
|
-
|
139
|
-
return "\n".join(docs)
|
140
|
-
|
141
|
-
@classmethod
|
142
|
-
def generate_system_prompt(
|
143
|
-
cls,
|
144
|
-
element_classes: List[Type[NotionBlockElement]],
|
145
|
-
) -> str:
|
146
|
-
"""
|
147
|
-
Generates a complete system prompt for LLMs.
|
148
|
-
"""
|
149
|
-
element_docs = cls.generate_element_docs(element_classes)
|
150
|
-
return cls.SYSTEM_PROMPT_TEMPLATE.format(element_docs=element_docs)
|