agentmark-templatedx 0.1.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.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentmark-templatedx
3
+ Version: 0.1.0
4
+ Summary: Python implementation of the AgentMark templatedx transformer
5
+ Project-URL: Homepage, https://github.com/agentmark/agentmark
6
+ Project-URL: Repository, https://github.com/agentmark/agentmark
7
+ Author: AgentMark Team
8
+ License-Expression: MIT
9
+ Keywords: agentmark,mdx,template,templatedx,transformer
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.12
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=1.0; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
21
+ Requires-Dist: pytest>=7.0; extra == 'dev'
22
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # AgentMark TemplateDX (Python)
26
+
27
+ Python implementation of the AgentMark templatedx transformer.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install agentmark-templatedx
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ This package transforms pre-parsed MDX AST trees. The AST is typically obtained by:
38
+ - Parsing MDX with the TypeScript `@agentmark-ai/templatedx` package
39
+ - Loading a pre-parsed AST from a JSON file
40
+ - Receiving an AST from the AgentMark runtime
41
+
42
+ ```python
43
+ import asyncio
44
+ import json
45
+ from templatedx import TemplateDX
46
+
47
+ async def main():
48
+ engine = TemplateDX()
49
+
50
+ # Load a pre-parsed MDX AST (from TypeScript parser or JSON file)
51
+ with open("template.ast.json") as f:
52
+ ast = json.load(f)
53
+
54
+ # Transform the AST with props
55
+ result = await engine.transform(
56
+ ast,
57
+ props={"name": "Alice", "items": [1, 2, 3]}
58
+ )
59
+
60
+ print(result)
61
+
62
+ asyncio.run(main())
63
+ ```
64
+
65
+ ## Custom Plugins
66
+
67
+ ```python
68
+ from templatedx import TagPlugin, PluginContext
69
+
70
+ class MyPlugin(TagPlugin):
71
+ async def transform(self, props, children, context):
72
+ # Transform children and return result
73
+ transformer = context.create_node_transformer(context.scope)
74
+ return await transformer.transform_children(children)
75
+
76
+ engine = TemplateDX()
77
+ engine.register_tag_plugin(MyPlugin(), ["MyTag"])
78
+ ```
79
+
80
+ ## Custom Filters
81
+
82
+ ```python
83
+ engine = TemplateDX()
84
+ engine.register_filter("double", lambda x: x * 2)
85
+ ```
86
+
87
+ ## Built-in Filters
88
+
89
+ - `capitalize(str)` - Capitalize first character
90
+ - `upper(str)` - Uppercase string
91
+ - `lower(str)` - Lowercase string
92
+ - `truncate(str, length)` - Truncate with ellipsis
93
+ - `abs(num)` - Absolute value
94
+ - `join(arr, separator)` - Join array elements
95
+ - `round(num, decimals)` - Round number
96
+ - `replace(str, search, replacement)` - Replace occurrences
97
+ - `urlencode(str)` - URL encode string
98
+ - `dump(any)` - JSON stringify
99
+
100
+ ## Built-in Tags
101
+
102
+ - `<If condition={...}>` / `<ElseIf condition={...}>` / `<Else>` - Conditional rendering
103
+ - `<ForEach arr={...}>{(item, index) => ...}</ForEach>` - Array iteration
104
+ - `<Raw>...</Raw>` - Raw content passthrough
@@ -0,0 +1,20 @@
1
+ templatedx/__init__.py,sha256=gvN3W7OwlxJ7s1n4e8AKcbE2n07jA3wssSxl1kqHBdM,1410
2
+ templatedx/constants.py,sha256=hBOTmHqhXaZ4BKmnH5LydMiurL4hBbtXKhPMu3AGSU8,650
3
+ templatedx/engine.py,sha256=GmEpybXMn2RVpYpqP775LnhSCmPKZpopTKEn8slXXy4,4849
4
+ templatedx/expression.py,sha256=ja5CFHke9rxjkPulwnm5o2lI6uQIAVZmJzXJSTEsgdw,23455
5
+ templatedx/filter_registry.py,sha256=gC1uk6-4eqfMC9jKYesKs1eqNN3SLvV2ibP2sfVT9nA,2409
6
+ templatedx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ templatedx/scope.py,sha256=R9mk84tbQig2RiyXo5_dIGnBkGGgLtqS83UEJ5CgF7Y,2743
8
+ templatedx/tag_plugin.py,sha256=6zcTeHBldvxhUASnsKQmtLkvE8TpZ7vf8OAkbhoPp0M,7246
9
+ templatedx/tag_registry.py,sha256=pv_Cr6zleW3CuojJ2kqSRo-EOifdTZK5_F4Gk4-dxiY,2504
10
+ templatedx/transformer.py,sha256=_WB_La9QrJZoGVZjI1KdArzCyL9GJJF152u_stzaMFc,7903
11
+ templatedx/utils.py,sha256=EViu19Sp3OGoets-BfAOvl0PK3AEEA-Wb5s6r9NxSk4,614
12
+ templatedx/filter_plugins/__init__.py,sha256=GVxR-HnEk0eI6qR1iUKECvbx-hVy0Kyg5sxX1YeJUVo,154
13
+ templatedx/filter_plugins/builtin.py,sha256=5b9Fa_pS_IQBbxeoG0NxTd_dYs7olRiACxZfT6SAVlg,4122
14
+ templatedx/tag_plugins/__init__.py,sha256=dXsiOGqI_lEvBlqUiILvadK67QRRO8e6BnqivDdqwqk,259
15
+ templatedx/tag_plugins/conditional.py,sha256=uV0ZCW3l_9_o7McNky07FrQZXNfR-ZZ4hQTftCoYXko,3369
16
+ templatedx/tag_plugins/for_each.py,sha256=LkvW4Tu-yN0Yj39sSPY8WQMO9NqjmsmOLct2VVoz_og,10699
17
+ templatedx/tag_plugins/raw.py,sha256=yq3J_XOxmzTWULE4CBaJ828SZa8OAOkaW65NTPh-l4Y,905
18
+ agentmark_templatedx-0.1.0.dist-info/METADATA,sha256=yPbj1RdmgT5F6TP67A-T4QMRUWPl8bkOxtY1npENXu8,2990
19
+ agentmark_templatedx-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
20
+ agentmark_templatedx-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
templatedx/__init__.py ADDED
@@ -0,0 +1,58 @@
1
+ """TemplateDX - Python implementation of the AgentMark templatedx transformer.
2
+
3
+ This package provides a Python implementation of the templatedx transformer
4
+ for processing AgentMark MDX AST trees.
5
+
6
+ Example:
7
+ ```python
8
+ import asyncio
9
+ from templatedx import TemplateDX
10
+
11
+ async def main():
12
+ engine = TemplateDX()
13
+ result = await engine.transform(
14
+ ast,
15
+ props={"name": "Alice"}
16
+ )
17
+ print(result)
18
+
19
+ asyncio.run(main())
20
+ ```
21
+ """
22
+
23
+ from .constants import MDX_JSX_ATTRIBUTE_TYPES, NODE_TYPES
24
+ from .engine import TemplateDX
25
+ from .expression import EvaluationError, ExpressionEvaluator, LexerError, ParseError
26
+ from .filter_registry import FilterRegistry
27
+ from .scope import Scope
28
+ from .tag_plugin import Node, NodeHelpers, PluginContext, TagPlugin
29
+ from .tag_registry import TagPluginRegistry
30
+ from .transformer import NodeTransformer, transform_tree
31
+
32
+ __version__ = "0.1.0"
33
+
34
+ __all__ = [
35
+ # Main engine
36
+ "TemplateDX",
37
+ # Core classes
38
+ "NodeTransformer",
39
+ "Scope",
40
+ "TagPlugin",
41
+ "PluginContext",
42
+ "NodeHelpers",
43
+ # Registries
44
+ "TagPluginRegistry",
45
+ "FilterRegistry",
46
+ # Expression evaluation
47
+ "ExpressionEvaluator",
48
+ "LexerError",
49
+ "ParseError",
50
+ "EvaluationError",
51
+ # Constants
52
+ "NODE_TYPES",
53
+ "MDX_JSX_ATTRIBUTE_TYPES",
54
+ # Types
55
+ "Node",
56
+ # Convenience functions
57
+ "transform_tree",
58
+ ]
@@ -0,0 +1,21 @@
1
+ """Constants for node types and attribute types."""
2
+
3
+ NODE_TYPES = {
4
+ "MDX_JSX_FLOW_ELEMENT": "mdxJsxFlowElement",
5
+ "MDX_JSX_TEXT_ELEMENT": "mdxJsxTextElement",
6
+ "MDX_JSX_ESM": "mdxjsEsm",
7
+ "YAML": "yaml",
8
+ "MDX_TEXT_EXPRESSION": "mdxTextExpression",
9
+ "MDX_FLOW_EXPRESSION": "mdxFlowExpression",
10
+ "LIST": "list",
11
+ "LIST_ITEM": "listItem",
12
+ "TEXT": "text",
13
+ "PARAGRAPH": "paragraph",
14
+ "HTML": "html",
15
+ }
16
+
17
+ MDX_JSX_ATTRIBUTE_TYPES = {
18
+ "MDX_JSX_ATTRIBUTE": "mdxJsxAttribute",
19
+ "MDX_JSX_ATTRIBUTE_VALUE_EXPRESSION": "mdxJsxAttributeValueExpression",
20
+ "MDX_JSX_EXPRESSION_ATTRIBUTE": "mdxJsxExpressionAttribute",
21
+ }
templatedx/engine.py ADDED
@@ -0,0 +1,155 @@
1
+ """TemplateDX engine - main entry point for the templatedx transformer."""
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any
5
+
6
+ from .filter_plugins import register_builtin_filters
7
+ from .filter_registry import FilterRegistry
8
+ from .scope import Scope
9
+ from .tag_plugin import Node, TagPlugin
10
+ from .tag_plugins import ElseIfPlugin, ElsePlugin, ForEachPlugin, IfPlugin, RawPlugin
11
+ from .tag_registry import TagPluginRegistry
12
+ from .transformer import NodeTransformer
13
+
14
+
15
+ def _register_builtin_tag_plugins() -> None:
16
+ """Register all built-in tag plugins globally."""
17
+ TagPluginRegistry.register_global(IfPlugin(), ["If"])
18
+ TagPluginRegistry.register_global(ElseIfPlugin(), ["ElseIf"])
19
+ TagPluginRegistry.register_global(ElsePlugin(), ["Else"])
20
+ TagPluginRegistry.register_global(ForEachPlugin(), ["ForEach"])
21
+ TagPluginRegistry.register_global(RawPlugin(), ["Raw"])
22
+
23
+
24
+ # Register built-in plugins on module load
25
+ _register_builtin_tag_plugins()
26
+ register_builtin_filters()
27
+
28
+
29
+ class TemplateDX:
30
+ """Stateful TemplateDX engine with isolated plugin registries.
31
+
32
+ This is the main entry point for using templatedx. It provides
33
+ instance-level plugin registries that inherit from global registries.
34
+
35
+ Example:
36
+ ```python
37
+ engine = TemplateDX()
38
+
39
+ # Transform an AST
40
+ result = await engine.transform(ast, props={"name": "Alice"})
41
+
42
+ # Register custom plugins
43
+ engine.register_tag_plugin(MyPlugin(), ["MyTag"])
44
+ engine.register_filter("double", lambda x: x * 2)
45
+ ```
46
+ """
47
+
48
+ def __init__(self) -> None:
49
+ """Initialize a new TemplateDX engine.
50
+
51
+ Creates instance-level registries that inherit from global registries.
52
+ """
53
+ self._tag_registry = TagPluginRegistry()
54
+ self._filter_registry = FilterRegistry()
55
+
56
+ # Copy built-in plugins to instance
57
+ self._tag_registry.copy_from_global()
58
+ self._filter_registry.copy_from_global()
59
+
60
+ def register_tag_plugin(self, plugin: TagPlugin, names: list[str]) -> None:
61
+ """Register a tag plugin on this instance.
62
+
63
+ Args:
64
+ plugin: The tag plugin instance
65
+ names: List of tag names this plugin handles
66
+ """
67
+ self._tag_registry.register(plugin, names)
68
+
69
+ def remove_tag_plugin(self, name: str) -> None:
70
+ """Remove a tag plugin from this instance.
71
+
72
+ Args:
73
+ name: Tag name to remove
74
+ """
75
+ self._tag_registry.remove(name)
76
+
77
+ def get_tag_plugin(self, name: str) -> TagPlugin | None:
78
+ """Get a tag plugin by name.
79
+
80
+ Args:
81
+ name: Tag name
82
+
83
+ Returns:
84
+ The plugin, or None if not found
85
+ """
86
+ return self._tag_registry.get(name)
87
+
88
+ def get_tag_registry(self) -> TagPluginRegistry:
89
+ """Get the tag plugin registry.
90
+
91
+ Returns:
92
+ The tag plugin registry for this instance
93
+ """
94
+ return self._tag_registry
95
+
96
+ def register_filter(self, name: str, func: Callable[..., Any]) -> None:
97
+ """Register a filter function on this instance.
98
+
99
+ Args:
100
+ name: Filter name
101
+ func: Filter function
102
+ """
103
+ self._filter_registry.register(name, func)
104
+
105
+ def remove_filter(self, name: str) -> None:
106
+ """Remove a filter from this instance.
107
+
108
+ Args:
109
+ name: Filter name to remove
110
+ """
111
+ self._filter_registry.remove(name)
112
+
113
+ def get_filter(self, name: str) -> Callable[..., Any] | None:
114
+ """Get a filter function by name.
115
+
116
+ Args:
117
+ name: Filter name
118
+
119
+ Returns:
120
+ The filter function, or None if not found
121
+ """
122
+ return self._filter_registry.get(name)
123
+
124
+ def get_filter_registry(self) -> FilterRegistry:
125
+ """Get the filter registry.
126
+
127
+ Returns:
128
+ The filter registry for this instance
129
+ """
130
+ return self._filter_registry
131
+
132
+ async def transform(
133
+ self,
134
+ tree: Node,
135
+ props: dict[str, Any] | None = None,
136
+ shared: dict[str, Any] | None = None,
137
+ ) -> Node:
138
+ """Transform an AST tree with the given props.
139
+
140
+ Note: Props are wrapped as {"props": props} to match TS behavior.
141
+ Templates access variables as `props.name`, not just `name`.
142
+
143
+ Args:
144
+ tree: Root AST node (pre-parsed MDX AST as dict)
145
+ props: Props to pass to the template
146
+ shared: Shared/global context accessible from all scopes
147
+
148
+ Returns:
149
+ Transformed AST tree
150
+ """
151
+ # Wrap props to match TypeScript behavior
152
+ variables = {"props": props if props is not None else {}}
153
+ scope = Scope(variables=variables, shared=shared if shared is not None else {})
154
+ transformer = NodeTransformer(scope, self)
155
+ return await transformer.transform(tree)