markdown-flow 0.4.1__tar.gz → 0.4.2.dev1__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.4.1 → markdown_flow-0.4.2.dev1}/PKG-INFO +3 -3
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/README.md +2 -2
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/__init__.py +1 -1
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/core.py +40 -23
- markdown_flow-0.4.2.dev1/markdown_flow/parser/document_blocks.py +678 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/document_context.py +54 -35
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/output.py +32 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/preprocessor.py +4 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow.egg-info/PKG-INFO +3 -3
- markdown_flow-0.4.1/markdown_flow/parser/document_blocks.py +0 -357
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/LICENSE +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/constants.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/constants_system_prompt.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/enums.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/escaping.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/exceptions.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/__init__.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/classifier.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/format.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/patterns.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/sanitize.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/stream.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/formatter/types.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/higher_priority_prompt.md +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/llm.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/models.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/__init__.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/code_fence_utils.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/html_comment_utils.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/interaction.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/json_parser.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/validation.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/parser/variable.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/providers/__init__.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/providers/config.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/providers/openai.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/system_prompt.md +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/tag_filter.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow/variable_names.py +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow.egg-info/SOURCES.txt +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow.egg-info/dependency_links.txt +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/markdown_flow.egg-info/top_level.txt +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/pyproject.toml +0 -0
- {markdown_flow-0.4.1 → markdown_flow-0.4.2.dev1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: markdown-flow
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2.dev1
|
|
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
|
|
@@ -708,8 +708,8 @@ literal. Only a leading assignment marker sets the interaction's variable;
|
|
|
708
708
|
markers elsewhere in its body remain literal option text. An unterminated
|
|
709
709
|
marker keeps the remaining text opaque, without restarting at an inner `?[`.
|
|
710
710
|
|
|
711
|
-
At document level, complete inline code, inline math, link resources,
|
|
712
|
-
code, and raw-text HTML bodies (`script`, `style`, `pre`, `textarea`) bound local
|
|
711
|
+
At document level, complete inline code, inline math, link resources, URI/email
|
|
712
|
+
angle autolinks, indented code, and raw-text HTML bodies (`script`, `style`, `pre`, `textarea`) bound local
|
|
713
713
|
variable examples. Quote/list-contained fences and ordinary HTML blocks also keep
|
|
714
714
|
unfinished examples local. Reference-definition destinations and titles retain
|
|
715
715
|
their complete variable examples. Physical CRLF, LF and CR boundaries preserve
|
|
@@ -690,8 +690,8 @@ literal. Only a leading assignment marker sets the interaction's variable;
|
|
|
690
690
|
markers elsewhere in its body remain literal option text. An unterminated
|
|
691
691
|
marker keeps the remaining text opaque, without restarting at an inner `?[`.
|
|
692
692
|
|
|
693
|
-
At document level, complete inline code, inline math, link resources,
|
|
694
|
-
code, and raw-text HTML bodies (`script`, `style`, `pre`, `textarea`) bound local
|
|
693
|
+
At document level, complete inline code, inline math, link resources, URI/email
|
|
694
|
+
angle autolinks, indented code, and raw-text HTML bodies (`script`, `style`, `pre`, `textarea`) bound local
|
|
695
695
|
variable examples. Quote/list-contained fences and ordinary HTML blocks also keep
|
|
696
696
|
unfinished examples local. Reference-definition destinations and titles retain
|
|
697
697
|
their complete variable examples. Physical CRLF, LF and CR boundaries preserve
|
|
@@ -71,9 +71,8 @@ def _translation_marker_spans(text: str) -> list[tuple[int, int]]:
|
|
|
71
71
|
return [(marker.start - (marker.start > 0 and text[marker.start - 1] == "%"), marker.end) for marker in find_variable_markers(text)]
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def _mask_translation_markers(text: str,
|
|
75
|
-
"""Replace whole markers
|
|
76
|
-
tokens = {raw: token for token, raw in markers.items()}
|
|
74
|
+
def _mask_translation_markers(text: str, tokens: dict[str, str]) -> str:
|
|
75
|
+
"""Replace whole markers using the interaction's shared raw-to-token index."""
|
|
77
76
|
parts: list[str] = []
|
|
78
77
|
at = 0
|
|
79
78
|
for start, end in _translation_marker_spans(text):
|
|
@@ -83,23 +82,29 @@ def _mask_translation_markers(text: str, markers: dict[str, str]) -> str:
|
|
|
83
82
|
return "".join(parts)
|
|
84
83
|
|
|
85
84
|
|
|
86
|
-
def
|
|
87
|
-
"""
|
|
85
|
+
def _translation_token_pattern(markers: dict[str, str]) -> re.Pattern[str] | None:
|
|
86
|
+
"""Match only the generated token namespace, without enumerating every token."""
|
|
87
|
+
if not markers:
|
|
88
|
+
return None
|
|
89
|
+
prefix = next(iter(markers)).rsplit("_", 3)[0] + "_"
|
|
90
|
+
return re.compile(re.escape(prefix) + r"[0-9]+__")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _restore_translation_markers(original: str, translated: Any, markers: dict[str, str], token_pattern: re.Pattern[str] | None = None) -> str:
|
|
94
|
+
"""Accept a translated field only when its known marker tokens survive in order."""
|
|
95
|
+
if not isinstance(translated, str):
|
|
96
|
+
translated = original
|
|
97
|
+
if not markers:
|
|
98
|
+
return translated
|
|
99
|
+
token_pattern = token_pattern if token_pattern is not None else _translation_token_pattern(markers)
|
|
100
|
+
assert token_pattern is not None
|
|
88
101
|
|
|
89
102
|
def token_sequence(text: str) -> list[str]:
|
|
90
|
-
|
|
91
|
-
for token in markers:
|
|
92
|
-
at = 0
|
|
93
|
-
while (at := text.find(token, at)) >= 0:
|
|
94
|
-
positions.append((at, token))
|
|
95
|
-
at += len(token)
|
|
96
|
-
return [token for _, token in sorted(positions)]
|
|
103
|
+
return [match.group() for match in token_pattern.finditer(text) if match.group() in markers]
|
|
97
104
|
|
|
98
|
-
if
|
|
105
|
+
if token_sequence(original) != token_sequence(translated):
|
|
99
106
|
translated = original
|
|
100
|
-
|
|
101
|
-
translated = translated.replace(token, raw)
|
|
102
|
-
return translated
|
|
107
|
+
return token_pattern.sub(lambda match: markers.get(match.group(), match.group()), translated)
|
|
103
108
|
|
|
104
109
|
|
|
105
110
|
def _find_translation_text(text: str, old: str) -> int:
|
|
@@ -1537,22 +1542,33 @@ class MarkdownFlow:
|
|
|
1537
1542
|
|
|
1538
1543
|
# Keep marker source out of the translation request. The prefix cannot
|
|
1539
1544
|
# collide with author text, and equal raw markers share the same token.
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1545
|
+
base = "__MDFLOW_VARIABLE_"
|
|
1546
|
+
used_nonces: set[str] = set()
|
|
1547
|
+
collision = False
|
|
1548
|
+
for match in re.finditer(re.escape(base) + r"(?:N([0-9]+)_)?", interaction_content):
|
|
1549
|
+
collision = True
|
|
1550
|
+
if match.group(1) is not None:
|
|
1551
|
+
used_nonces.add(match.group(1))
|
|
1552
|
+
prefix = base
|
|
1553
|
+
if collision:
|
|
1554
|
+
nonce = 0
|
|
1555
|
+
while str(nonce) in used_nonces:
|
|
1556
|
+
nonce += 1
|
|
1557
|
+
prefix = f"{base}N{nonce}_"
|
|
1543
1558
|
raw_markers = dict.fromkeys(interaction_content[start:end] for start, end in _translation_marker_spans(interaction_content))
|
|
1544
1559
|
markers = {f"{prefix}{i}__": raw for i, raw in enumerate(raw_markers)}
|
|
1545
1560
|
interaction_info["_translation_markers"] = markers
|
|
1561
|
+
tokens_by_raw = {raw: token for token, raw in markers.items()}
|
|
1546
1562
|
translatable: dict[str, str | list[str]] = {}
|
|
1547
1563
|
|
|
1548
1564
|
# Extract button display text
|
|
1549
1565
|
if interaction_info.get("buttons"):
|
|
1550
|
-
button_texts = [_mask_translation_markers(btn["display"],
|
|
1566
|
+
button_texts = [_mask_translation_markers(btn["display"], tokens_by_raw) for btn in interaction_info["buttons"]]
|
|
1551
1567
|
translatable["buttons"] = button_texts
|
|
1552
1568
|
|
|
1553
1569
|
# Extract question text
|
|
1554
1570
|
if interaction_info.get("question"):
|
|
1555
|
-
translatable["question"] = _mask_translation_markers(interaction_info["question"],
|
|
1571
|
+
translatable["question"] = _mask_translation_markers(interaction_info["question"], tokens_by_raw)
|
|
1556
1572
|
|
|
1557
1573
|
# Convert to JSON
|
|
1558
1574
|
|
|
@@ -1627,6 +1643,7 @@ class MarkdownFlow:
|
|
|
1627
1643
|
return original_content
|
|
1628
1644
|
|
|
1629
1645
|
markers = interaction_info.get("_translation_markers", {})
|
|
1646
|
+
token_pattern = _translation_token_pattern(markers)
|
|
1630
1647
|
prefix = ""
|
|
1631
1648
|
reconstructed = original_content
|
|
1632
1649
|
marker = COMPILED_LAYER2_VARIABLE_REGEX.match(original_content[2:-1]) if original_content.startswith("?[") else None
|
|
@@ -1640,7 +1657,7 @@ class MarkdownFlow:
|
|
|
1640
1657
|
if i < len(translated["buttons"]):
|
|
1641
1658
|
old_display = button["display"]
|
|
1642
1659
|
original_display = original.get("buttons", [])[i]
|
|
1643
|
-
new_display = _restore_translation_markers(original_display, translated["buttons"][i], markers)
|
|
1660
|
+
new_display = _restore_translation_markers(original_display, translated["buttons"][i], markers, token_pattern)
|
|
1644
1661
|
|
|
1645
1662
|
# Detect if translation happened
|
|
1646
1663
|
translation_happened = False
|
|
@@ -1680,7 +1697,7 @@ class MarkdownFlow:
|
|
|
1680
1697
|
# Replace question text
|
|
1681
1698
|
if "question" in translated and interaction_info.get("question"):
|
|
1682
1699
|
old_question = interaction_info["question"]
|
|
1683
|
-
new_question = _restore_translation_markers(original["question"], translated["question"], markers)
|
|
1700
|
+
new_question = _restore_translation_markers(original["question"], translated["question"], markers, token_pattern)
|
|
1684
1701
|
reconstructed = _replace_translation_text(reconstructed, f"...{old_question}", f"...{new_question}")
|
|
1685
1702
|
|
|
1686
1703
|
return prefix + reconstructed
|