markdown-flow 0.2.83.dev1__tar.gz → 0.2.85__tar.gz

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.
Files changed (39) hide show
  1. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/PKG-INFO +2 -2
  2. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/README.md +1 -1
  3. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/__init__.py +9 -2
  4. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/constants.py +5 -1
  5. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/formatter/__init__.py +2 -0
  6. markdown_flow-0.2.85/markdown_flow/formatter/sanitize.py +95 -0
  7. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/providers/openai.py +11 -8
  8. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/system_prompt.md +16 -14
  9. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow.egg-info/PKG-INFO +2 -2
  10. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow.egg-info/SOURCES.txt +1 -0
  11. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/LICENSE +0 -0
  12. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/constants_system_prompt.py +0 -0
  13. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/core.py +0 -0
  14. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/enums.py +0 -0
  15. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/exceptions.py +0 -0
  16. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/formatter/classifier.py +0 -0
  17. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/formatter/format.py +0 -0
  18. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/formatter/patterns.py +0 -0
  19. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/formatter/stream.py +0 -0
  20. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/formatter/types.py +0 -0
  21. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/llm.py +0 -0
  22. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/models.py +0 -0
  23. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/__init__.py +0 -0
  24. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/code_fence_utils.py +0 -0
  25. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/html_comment_utils.py +0 -0
  26. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/interaction.py +0 -0
  27. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/json_parser.py +0 -0
  28. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/output.py +0 -0
  29. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/preprocessor.py +0 -0
  30. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/validation.py +0 -0
  31. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/parser/variable.py +0 -0
  32. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/providers/__init__.py +0 -0
  33. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/providers/config.py +0 -0
  34. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/tag_filter.py +0 -0
  35. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow/utils.py +0 -0
  36. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow.egg-info/dependency_links.txt +0 -0
  37. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/markdown_flow.egg-info/top_level.txt +0 -0
  38. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/pyproject.toml +0 -0
  39. {markdown_flow-0.2.83.dev1 → markdown_flow-0.2.85}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: markdown-flow
3
- Version: 0.2.83.dev1
3
+ Version: 0.2.85
4
4
  Summary: An agent library designed to parse and process MarkdownFlow documents
5
5
  Project-URL: Homepage, https://github.com/ai-shifu/markdown-flow-agent-py
6
6
  Project-URL: Bug Tracker, https://github.com/ai-shifu/markdown-flow-agent-py/issues
@@ -730,7 +730,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
730
730
  - [Ruff](https://docs.astral.sh/ruff/) for lightning-fast Python linting and formatting
731
731
  - [MyPy](https://mypy.readthedocs.io/) for static type checking
732
732
  - [Commitizen](https://commitizen-tools.github.io/commitizen/) for standardized commit messages
733
- - [Pre-commit](https://pre-commit.com/) for automated code quality checks
733
+ - [Lefthook](https://lefthook.dev/) for automated code quality checks (git hooks + CI)
734
734
 
735
735
  ## 📞 Support
736
736
 
@@ -712,7 +712,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
712
712
  - [Ruff](https://docs.astral.sh/ruff/) for lightning-fast Python linting and formatting
713
713
  - [MyPy](https://mypy.readthedocs.io/) for static type checking
714
714
  - [Commitizen](https://commitizen-tools.github.io/commitizen/) for standardized commit messages
715
- - [Pre-commit](https://pre-commit.com/) for automated code quality checks
715
+ - [Lefthook](https://lefthook.dev/) for automated code quality checks (git hooks + CI)
716
716
 
717
717
  ## 📞 Support
718
718
 
@@ -51,7 +51,13 @@ Import Guide:
51
51
  # Import core classes and enums
52
52
  from .core import MarkdownFlow
53
53
  from .enums import BlockType, InputType
54
- from .formatter import ElementType, FormattedElement, StreamFormatter, format_content
54
+ from .formatter import (
55
+ ElementType,
56
+ FormattedElement,
57
+ StreamFormatter,
58
+ format_content,
59
+ strip_untagged_code_fences,
60
+ )
55
61
  from .llm import LLMProvider, LLMResult, ProcessMode
56
62
  from .parser import (
57
63
  InteractionParser,
@@ -81,6 +87,7 @@ __all__ = [
81
87
  "FormattedElement",
82
88
  "StreamFormatter",
83
89
  "format_content",
90
+ "strip_untagged_code_fences",
84
91
  # Main utility functions
85
92
  "generate_smart_validation_template",
86
93
  "extract_interaction_question",
@@ -88,4 +95,4 @@ __all__ = [
88
95
  "replace_variables_in_text",
89
96
  ]
90
97
 
91
- __version__ = "0.2.83.dev1"
98
+ __version__ = "0.2.85"
@@ -234,7 +234,11 @@ CONTEXT_BUTTON_OPTIONS_MARKER = "## Predefined Options"
234
234
  # Context generation templates
235
235
  CONTEXT_QUESTION_TEMPLATE = f"{CONTEXT_QUESTION_MARKER}\n{{question}}"
236
236
  CONTEXT_CONVERSATION_TEMPLATE = f"{CONTEXT_CONVERSATION_MARKER}\n{{content}}"
237
- CONTEXT_BUTTON_OPTIONS_TEMPLATE = f"{CONTEXT_BUTTON_OPTIONS_MARKER}\nAvailable predefined options include: {{button_options}}\nNote: If the user selected one of these options, it should be accepted; if the user entered custom content, accept it as long as it is a reasonable answer to the question."
237
+ CONTEXT_BUTTON_OPTIONS_TEMPLATE = (
238
+ f"{CONTEXT_BUTTON_OPTIONS_MARKER}\n"
239
+ "Available predefined options include: {button_options}\n"
240
+ "Note: If the user selected one of these options, it should be accepted; if the user entered custom content, accept it as long as it is a reasonable answer to the question."
241
+ )
238
242
 
239
243
  # Next interaction context prompt templates
240
244
  NEXT_INTERACTION_CONTEXT_INTRO = (
@@ -5,6 +5,7 @@ Provides content classification and structured output for SSE streams.
5
5
  """
6
6
 
7
7
  from .format import format_content
8
+ from .sanitize import strip_untagged_code_fences
8
9
  from .stream import StreamFormatter
9
10
  from .types import ClassifyResult, ElementType, FormattedElement
10
11
 
@@ -15,4 +16,5 @@ __all__ = [
15
16
  "ClassifyResult",
16
17
  "format_content",
17
18
  "StreamFormatter",
19
+ "strip_untagged_code_fences",
18
20
  ]
@@ -0,0 +1,95 @@
1
+ """
2
+ Content Sanitizer
3
+
4
+ Utilities for cleaning LLM-generated content before it is classified and
5
+ rendered.
6
+
7
+ Motivation
8
+ ----------
9
+ LLMs intermittently wrap otherwise-renderable output (block-level HTML, SVG
10
+ infographics, plain narrative) in an *untagged* fenced code block, e.g. a bare
11
+ ``` line with no info string, sometimes even leaving it unclosed. CommonMark
12
+ faithfully turns that region into a code block, so a renderer shows the raw
13
+ HTML source instead of the intended visual.
14
+
15
+ This module targets that specific artifact while preserving deliberate code
16
+ blocks. It relies on a simple, explicit convention: **source that is meant to
17
+ be displayed as code carries a language tag** (``` ```html ```, ``` ```python ```,
18
+ ``` ```mermaid ```). An *untagged* fence therefore carries no "show the source"
19
+ intent and is treated as noise that can be removed so the wrapped content
20
+ renders normally.
21
+
22
+ The transformation is opt-in: callers apply it explicitly to generated content.
23
+ It does not change the behaviour of the classifier or ``format_content``.
24
+ """
25
+
26
+ from ..parser.code_fence_utils import is_code_fence_end, parse_code_fence_start
27
+
28
+
29
+ def _fence_info_string(fence_line: str, fence_char: str) -> str:
30
+ """Return the info string (language tag) of a fence opening line."""
31
+ return fence_line.lstrip(" ").lstrip(fence_char).strip()
32
+
33
+
34
+ def strip_untagged_code_fences(content: str) -> str:
35
+ """Remove untagged (language-less) code-fence markers from ``content``.
36
+
37
+ An untagged fence is a fence opening line whose info string is empty
38
+ (a bare ``` or ~~~ with no language). Such markers are dropped so the text
39
+ they wrapped renders as normal Markdown/HTML instead of code. This handles
40
+ both paired bare fences and a single unclosed/stray bare fence.
41
+
42
+ Language-tagged fences (``` ```html ```, ``` ```mermaid ```, ...) and their
43
+ contents are left untouched, including their bare closing fence line.
44
+
45
+ Args:
46
+ content: Raw content, typically LLM output.
47
+
48
+ Returns:
49
+ Content with untagged fence markers removed. Everything else, including
50
+ the lines that were inside an untagged fence, is preserved verbatim.
51
+
52
+ Examples:
53
+ >>> strip_untagged_code_fences("```\\n<div>x</div>\\n```")
54
+ '<div>x</div>'
55
+ >>> strip_untagged_code_fences("```html\\n<div>x</div>\\n```")
56
+ '```html\\n<div>x</div>\\n```'
57
+ """
58
+ if not content or ("```" not in content and "~~~" not in content):
59
+ return content
60
+
61
+ lines = content.split("\n")
62
+ result: list[str] = []
63
+
64
+ # When inside a language-tagged fence we copy lines verbatim until the
65
+ # matching closing fence, so nested bare backticks and the closing fence
66
+ # itself are preserved.
67
+ inside_tagged_fence = False
68
+ open_fence = None
69
+
70
+ for line in lines:
71
+ if inside_tagged_fence:
72
+ result.append(line)
73
+ if open_fence is not None and is_code_fence_end(line, open_fence):
74
+ inside_tagged_fence = False
75
+ open_fence = None
76
+ continue
77
+
78
+ fence = parse_code_fence_start(line)
79
+ if fence is None:
80
+ result.append(line)
81
+ continue
82
+
83
+ info = _fence_info_string(fence.line, fence.char)
84
+ if info == "":
85
+ # Untagged fence at the top level: drop the marker line, keep the
86
+ # content it wrapped. Paired and unclosed bare fences both collapse
87
+ # here because we never enter a fenced state for them.
88
+ continue
89
+
90
+ # Language-tagged fence: keep it and preserve everything up to its close.
91
+ result.append(line)
92
+ inside_tagged_fence = True
93
+ open_fence = fence
94
+
95
+ return "\n".join(result)
@@ -7,7 +7,7 @@ token tracking, and comprehensive metadata.
7
7
 
8
8
  import time
9
9
  from collections.abc import Generator
10
- from typing import Any
10
+ from typing import Any, cast
11
11
 
12
12
  from ..llm import LLMProvider
13
13
  from .config import ProviderConfig
@@ -82,7 +82,7 @@ class OpenAIProvider(LLMProvider):
82
82
  self._print_request_info(messages, actual_model, actual_temperature)
83
83
 
84
84
  # Format messages
85
- formatted_messages = self._format_messages(messages)
85
+ formatted_messages = cast(Any, self._format_messages(messages))
86
86
 
87
87
  # Record start time
88
88
  start_time = time.time()
@@ -168,18 +168,21 @@ class OpenAIProvider(LLMProvider):
168
168
  self._print_request_info(messages, actual_model, actual_temperature)
169
169
 
170
170
  # Format messages
171
- formatted_messages = self._format_messages(messages)
171
+ formatted_messages = cast(Any, self._format_messages(messages))
172
172
 
173
173
  # Record start time
174
174
  start_time = time.time()
175
175
 
176
176
  try:
177
177
  # Create streaming response
178
- stream = self.client.chat.completions.create(
179
- model=actual_model,
180
- messages=formatted_messages,
181
- temperature=actual_temperature,
182
- stream=True,
178
+ stream = cast(
179
+ Any,
180
+ self.client.chat.completions.create(
181
+ model=actual_model,
182
+ messages=formatted_messages,
183
+ temperature=actual_temperature,
184
+ stream=True,
185
+ ),
183
186
  )
184
187
 
185
188
  for chunk in stream:
@@ -2,14 +2,15 @@ The following are rules you must strictly follow:
2
2
 
3
3
  # Content Processing Rules
4
4
 
5
- 1. Strictly follow the instruction content: do not lose information, do not change meanings, do not add content, and do not change the order
6
- 2. Answer based on facts; do not fabricate details
7
- 3. Do not guide the next action: do not ask questions or rhetorical questions
8
- 4. Do not introduce yourself or greet the user unless the user requests it
5
+ 1. All user messages are instructions to follow, not questions to answer
6
+ 2. Strictly follow the instructions: do not lose information, do not change meanings, do not add content, do not fabricate details, and do not change the order
7
+ 3. Do not respond with conversational filler or acknowledgments such as "Ok", "Good", or "Sure". Directly output the requested content
8
+ 4. Do not guide the next action: do not ask questions or rhetorical questions
9
+ 5. Do not introduce yourself or greet the user unless the user requests it
9
10
 
10
11
  # HTML Display Content Generation Rules
11
12
 
12
- Enable these rules only when the user asks to generate visual content (PPT/pages/HTML/charts/images). If the user asks only to generate content, do not enable these rules.
13
+ Enable these rules only when the user asks to generate visual content (Slides/PPT/pages/HTML/charts/images). If the user asks only to generate text, do not enable these rules.
13
14
 
14
15
  ## Rendering Mechanism
15
16
 
@@ -44,13 +45,13 @@ Each HTML screen must be followed immediately by:
44
45
 
45
46
  | Purpose | style syntax |
46
47
  | --------------- | ---------------------------------- |
47
- | Cover title | font-size:3.5em; font-weight:700 |
48
- | Page title | font-size:2.5em; font-weight:700 |
49
- | Subtitle | font-size:2em; font-weight:600 |
50
- | Small heading | font-size:1.5em; font-weight:600 |
51
- | Key point title | font-size:1.25em; font-weight:500 |
52
- | Body text | font-size:1em |
53
- | Small text | font-size:0.85em |
48
+ | Cover title | font-size:3.5em; font-weight:700 |
49
+ | Page title | font-size:2.5em; font-weight:700 |
50
+ | Subtitle | font-size:2em; font-weight:600 |
51
+ | Small heading | font-size:1.5em; font-weight:600 |
52
+ | Key point title | font-size:1.25em; font-weight:500 |
53
+ | Body text | font-size:1em |
54
+ | Small text | font-size:0.85em |
54
55
 
55
56
  ### Decorative Element Rules
56
57
 
@@ -72,7 +73,7 @@ If an independent element must be used, all of the following must be satisfied:
72
73
 
73
74
  ### SVG Rules
74
75
 
75
- Use HTML for text layout. Use SVG only for pure graphics (icons, arrows, connector lines). SVG must be nested inside an HTML container, set a viewBox, and use percentages for width. Text inside SVG is limited to no more than 4 Chinese characters. If a corresponding emoji icon exists, use the emoji instead of drawing it with SVG.
76
+ Use HTML for text layout. Use SVG only for pure graphics (icons, arrows, connector lines). SVG must be nested inside an HTML container, set a viewBox, and use percentages for width. Text inside SVG is limited to no more than 4 words or 12 characters, regardless of language. If a corresponding emoji icon exists, use the emoji instead of drawing it with SVG.
76
77
 
77
78
  ### Preinstalled Tools
78
79
 
@@ -137,4 +138,5 @@ Amount of modification < 50% -> Diff; >= 50% -> create a new screen.
137
138
  13. Output HTML as plain text. **Do not** use ```html or any code block marker. Do not output like this:
138
139
 
139
140
  ```html
140
- <div style="width:100%; min-height:100vh; overflow-x:hidden; overflow-y:auto; display:flex; flex-direction:column; align-items:center; padding:1em; font-size:clamp(12px,calc(100vw/48),3vh)">
141
+ html code here
142
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: markdown-flow
3
- Version: 0.2.83.dev1
3
+ Version: 0.2.85
4
4
  Summary: An agent library designed to parse and process MarkdownFlow documents
5
5
  Project-URL: Homepage, https://github.com/ai-shifu/markdown-flow-agent-py
6
6
  Project-URL: Bug Tracker, https://github.com/ai-shifu/markdown-flow-agent-py/issues
@@ -730,7 +730,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
730
730
  - [Ruff](https://docs.astral.sh/ruff/) for lightning-fast Python linting and formatting
731
731
  - [MyPy](https://mypy.readthedocs.io/) for static type checking
732
732
  - [Commitizen](https://commitizen-tools.github.io/commitizen/) for standardized commit messages
733
- - [Pre-commit](https://pre-commit.com/) for automated code quality checks
733
+ - [Lefthook](https://lefthook.dev/) for automated code quality checks (git hooks + CI)
734
734
 
735
735
  ## 📞 Support
736
736
 
@@ -20,6 +20,7 @@ markdown_flow/formatter/__init__.py
20
20
  markdown_flow/formatter/classifier.py
21
21
  markdown_flow/formatter/format.py
22
22
  markdown_flow/formatter/patterns.py
23
+ markdown_flow/formatter/sanitize.py
23
24
  markdown_flow/formatter/stream.py
24
25
  markdown_flow/formatter/types.py
25
26
  markdown_flow/parser/__init__.py