markdown-flow 0.2.80__tar.gz → 0.2.81__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.
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/PKG-INFO +1 -1
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/__init__.py +1 -1
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/core.py +17 -8
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/system_prompt.md +2 -2
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow.egg-info/PKG-INFO +1 -1
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow.egg-info/SOURCES.txt +1 -0
- markdown_flow-0.2.81/tests/test_error_render_context.py +58 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/LICENSE +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/README.md +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/constants.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/constants_system_prompt.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/enums.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/exceptions.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/formatter/__init__.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/formatter/classifier.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/formatter/format.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/formatter/patterns.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/formatter/stream.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/formatter/types.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/llm.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/models.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/__init__.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/code_fence_utils.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/html_comment_utils.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/interaction.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/json_parser.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/output.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/preprocessor.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/validation.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/parser/variable.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/providers/__init__.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/providers/config.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/providers/openai.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/tag_filter.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow/utils.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow.egg-info/dependency_links.txt +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/markdown_flow.egg-info/top_level.txt +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/pyproject.toml +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/setup.cfg +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_dynamic_interaction.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_formatter.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_formatter_stream.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_html_comment_utils.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_markdownflow_basic.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_parser_interaction.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_parser_output.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_parser_variable.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_preprocessor.py +0 -0
- {markdown_flow-0.2.80 → markdown_flow-0.2.81}/tests/test_preserved_simple.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: markdown-flow
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.81
|
|
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
|
|
@@ -803,7 +803,7 @@ class MarkdownFlow:
|
|
|
803
803
|
# Basic validation
|
|
804
804
|
if not user_input or not any(values for values in user_input.values()):
|
|
805
805
|
error_msg = INPUT_EMPTY_ERROR
|
|
806
|
-
return self._render_error(error_msg, mode, context)
|
|
806
|
+
return self._render_error(error_msg, mode, context, variables)
|
|
807
807
|
|
|
808
808
|
# Get the target variable value from user_input
|
|
809
809
|
target_values = user_input.get(target_variable, [])
|
|
@@ -817,7 +817,7 @@ class MarkdownFlow:
|
|
|
817
817
|
|
|
818
818
|
if "error" in parse_result:
|
|
819
819
|
error_msg = INTERACTION_PARSE_ERROR.format(error=parse_result["error"])
|
|
820
|
-
return self._render_error(error_msg, mode, context)
|
|
820
|
+
return self._render_error(error_msg, mode, context, variables)
|
|
821
821
|
|
|
822
822
|
interaction_type = parse_result.get("type")
|
|
823
823
|
|
|
@@ -942,6 +942,7 @@ class MarkdownFlow:
|
|
|
942
942
|
mode,
|
|
943
943
|
interaction_type,
|
|
944
944
|
context,
|
|
945
|
+
variables,
|
|
945
946
|
)
|
|
946
947
|
|
|
947
948
|
if interaction_type == InteractionType.NON_ASSIGNMENT_BUTTON:
|
|
@@ -995,7 +996,7 @@ class MarkdownFlow:
|
|
|
995
996
|
context=context,
|
|
996
997
|
)
|
|
997
998
|
error_msg = f"No input provided for variable '{target_variable}'"
|
|
998
|
-
return self._render_error(error_msg, mode, context)
|
|
999
|
+
return self._render_error(error_msg, mode, context, variables)
|
|
999
1000
|
|
|
1000
1001
|
def _match_button_values(
|
|
1001
1002
|
self,
|
|
@@ -1038,6 +1039,7 @@ class MarkdownFlow:
|
|
|
1038
1039
|
mode: ProcessMode,
|
|
1039
1040
|
interaction_type: InteractionType,
|
|
1040
1041
|
context: list[dict[str, str]] | None = None,
|
|
1042
|
+
variables: dict[str, str | list[str]] | None = None,
|
|
1041
1043
|
) -> LLMResult | Generator[LLMResult, None, None]:
|
|
1042
1044
|
"""
|
|
1043
1045
|
Simplified button validation with new input format.
|
|
@@ -1082,7 +1084,7 @@ class MarkdownFlow:
|
|
|
1082
1084
|
# Pure button mode requires input
|
|
1083
1085
|
button_displays = [btn["display"] for btn in buttons]
|
|
1084
1086
|
error_msg = f"Please select from: {', '.join(button_displays)}"
|
|
1085
|
-
return self._render_error(error_msg, mode, context)
|
|
1087
|
+
return self._render_error(error_msg, mode, context, variables)
|
|
1086
1088
|
|
|
1087
1089
|
# Validate input values against available buttons
|
|
1088
1090
|
valid_values = []
|
|
@@ -1107,7 +1109,7 @@ class MarkdownFlow:
|
|
|
1107
1109
|
if invalid_values and not allow_text_input:
|
|
1108
1110
|
button_displays = [btn["display"] for btn in buttons]
|
|
1109
1111
|
error_msg = f"Invalid options: {', '.join(invalid_values)}. Please select from: {', '.join(button_displays)}"
|
|
1110
|
-
return self._render_error(error_msg, mode, context)
|
|
1112
|
+
return self._render_error(error_msg, mode, context, variables)
|
|
1111
1113
|
|
|
1112
1114
|
# Success: return validated values
|
|
1113
1115
|
result = LLMResult(
|
|
@@ -1180,13 +1182,14 @@ class MarkdownFlow:
|
|
|
1180
1182
|
error_message: str,
|
|
1181
1183
|
mode: ProcessMode,
|
|
1182
1184
|
context: list[dict[str, str]] | None = None,
|
|
1185
|
+
variables: dict[str, str | list[str]] | None = None,
|
|
1183
1186
|
) -> LLMResult | Generator[LLMResult, None, None]:
|
|
1184
1187
|
"""Render user-friendly error message."""
|
|
1185
1188
|
# Truncate context to configured maximum length
|
|
1186
1189
|
truncated_context = self._truncate_context(context)
|
|
1187
1190
|
|
|
1188
1191
|
# Build error messages with context
|
|
1189
|
-
messages = self._build_error_render_messages(error_message, truncated_context)
|
|
1192
|
+
messages = self._build_error_render_messages(error_message, truncated_context, variables)
|
|
1190
1193
|
|
|
1191
1194
|
if mode == ProcessMode.COMPLETE:
|
|
1192
1195
|
if not self._llm_provider:
|
|
@@ -1531,6 +1534,7 @@ class MarkdownFlow:
|
|
|
1531
1534
|
self,
|
|
1532
1535
|
error_message: str,
|
|
1533
1536
|
context: list[dict[str, str]] | None = None,
|
|
1537
|
+
variables: dict[str, str | list[str]] | None = None,
|
|
1534
1538
|
) -> list[dict[str, str]]:
|
|
1535
1539
|
"""Build error rendering messages."""
|
|
1536
1540
|
render_prompt = f"""{self._interaction_error_prompt}
|
|
@@ -1545,10 +1549,15 @@ Original Error: {error_message}
|
|
|
1545
1549
|
|
|
1546
1550
|
messages.append({"role": "system", "content": render_prompt})
|
|
1547
1551
|
|
|
1548
|
-
# Add conversation history context if provided
|
|
1552
|
+
# Add conversation history context if provided.
|
|
1553
|
+
# Transform interaction syntax in the context the same way the content
|
|
1554
|
+
# path does, so raw ?[...] blocks are expanded to {user}/{assistant}
|
|
1555
|
+
# pairs instead of leaking into this auxiliary LLM call.
|
|
1549
1556
|
truncated_context = self._truncate_context(context)
|
|
1550
1557
|
if truncated_context:
|
|
1551
|
-
|
|
1558
|
+
transformed_context = self._transform_context_messages(truncated_context, variables)
|
|
1559
|
+
if transformed_context:
|
|
1560
|
+
messages.extend(transformed_context)
|
|
1552
1561
|
|
|
1553
1562
|
messages.append({"role": "user", "content": error_message})
|
|
1554
1563
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
每屏 = 一个铺满视口的固定容器,不可滚动。外层容器写法:
|
|
28
28
|
|
|
29
|
-
```
|
|
29
|
+
```text
|
|
30
30
|
<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)">
|
|
31
31
|
<!-- 内容 -->
|
|
32
32
|
</div>
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
每屏 HTML 后必须紧跟:
|
|
36
36
|
|
|
37
|
-
```
|
|
37
|
+
```text
|
|
38
38
|
<style>
|
|
39
39
|
*,*::before,*::after{box-sizing:border-box;overflow-wrap:break-word;word-wrap:break-word}
|
|
40
40
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: markdown-flow
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.81
|
|
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
|
|
@@ -35,6 +35,7 @@ markdown_flow/providers/__init__.py
|
|
|
35
35
|
markdown_flow/providers/config.py
|
|
36
36
|
markdown_flow/providers/openai.py
|
|
37
37
|
tests/test_dynamic_interaction.py
|
|
38
|
+
tests/test_error_render_context.py
|
|
38
39
|
tests/test_formatter.py
|
|
39
40
|
tests/test_formatter_stream.py
|
|
40
41
|
tests/test_html_comment_utils.py
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for context transformation in the error-render path.
|
|
3
|
+
|
|
4
|
+
The error-render path is an auxiliary LLM call triggered when interaction
|
|
5
|
+
input validation fails. It must transform interaction syntax in the provided
|
|
6
|
+
context the same way the content path does, so raw ?[...] blocks are expanded
|
|
7
|
+
into {user}/{assistant} pairs instead of leaking into the prompt.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from markdown_flow import MarkdownFlow
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _build_messages(context, variables):
|
|
14
|
+
mf = MarkdownFlow("Doc", llm_provider=None)
|
|
15
|
+
truncated = mf._truncate_context(context)
|
|
16
|
+
return mf._build_error_render_messages("some error", truncated, variables)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_error_render_expands_interaction_with_variable():
|
|
20
|
+
"""An interaction with a resolvable variable becomes user(value)+assistant(ok)."""
|
|
21
|
+
context = [
|
|
22
|
+
{"role": "user", "content": "What is your name?"},
|
|
23
|
+
{"role": "assistant", "content": "?[%{{nickname}} ...What is your nickname?]"},
|
|
24
|
+
]
|
|
25
|
+
variables = {"nickname": "Alice"}
|
|
26
|
+
|
|
27
|
+
messages = _build_messages(context, variables)
|
|
28
|
+
|
|
29
|
+
# Raw interaction syntax must not leak into the prompt.
|
|
30
|
+
assert all("?[" not in m["content"] for m in messages)
|
|
31
|
+
# The interaction is expanded to a clean user/assistant pair.
|
|
32
|
+
assert {"role": "user", "content": "Alice"} in messages
|
|
33
|
+
expanded = [m for m in messages if m["role"] == "assistant" and m["content"] == "ok"]
|
|
34
|
+
assert expanded, "expected an assistant 'ok' acknowledgement"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_error_render_skips_interaction_without_value():
|
|
38
|
+
"""A variable interaction with no resolvable value is dropped, not leaked."""
|
|
39
|
+
context = [
|
|
40
|
+
{"role": "assistant", "content": "?[%{{nickname}} ...What is your nickname?]"},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
messages = _build_messages(context, variables={})
|
|
44
|
+
|
|
45
|
+
assert all("?[" not in m["content"] for m in messages)
|
|
46
|
+
assert all(m["content"] != "Alice" for m in messages)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_error_render_no_variable_interaction_becomes_ok():
|
|
50
|
+
"""A button-only interaction (no variable) becomes ok/ok."""
|
|
51
|
+
context = [
|
|
52
|
+
{"role": "assistant", "content": "?[Continue|Cancel]"},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
messages = _build_messages(context, variables=None)
|
|
56
|
+
|
|
57
|
+
assert all("?[" not in m["content"] for m in messages)
|
|
58
|
+
assert {"role": "user", "content": "ok"} in messages
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|