hexdag 0.5.0.dev1__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.
Files changed (261) hide show
  1. hexdag/__init__.py +116 -0
  2. hexdag/__main__.py +30 -0
  3. hexdag/adapters/executors/__init__.py +5 -0
  4. hexdag/adapters/executors/local_executor.py +316 -0
  5. hexdag/builtin/__init__.py +6 -0
  6. hexdag/builtin/adapters/__init__.py +51 -0
  7. hexdag/builtin/adapters/anthropic/__init__.py +5 -0
  8. hexdag/builtin/adapters/anthropic/anthropic_adapter.py +151 -0
  9. hexdag/builtin/adapters/database/__init__.py +6 -0
  10. hexdag/builtin/adapters/database/csv/csv_adapter.py +249 -0
  11. hexdag/builtin/adapters/database/pgvector/__init__.py +5 -0
  12. hexdag/builtin/adapters/database/pgvector/pgvector_adapter.py +478 -0
  13. hexdag/builtin/adapters/database/sqlalchemy/sqlalchemy_adapter.py +252 -0
  14. hexdag/builtin/adapters/database/sqlite/__init__.py +5 -0
  15. hexdag/builtin/adapters/database/sqlite/sqlite_adapter.py +410 -0
  16. hexdag/builtin/adapters/local/README.md +59 -0
  17. hexdag/builtin/adapters/local/__init__.py +7 -0
  18. hexdag/builtin/adapters/local/local_observer_manager.py +696 -0
  19. hexdag/builtin/adapters/memory/__init__.py +47 -0
  20. hexdag/builtin/adapters/memory/file_memory_adapter.py +297 -0
  21. hexdag/builtin/adapters/memory/in_memory_memory.py +216 -0
  22. hexdag/builtin/adapters/memory/schemas.py +57 -0
  23. hexdag/builtin/adapters/memory/session_memory.py +178 -0
  24. hexdag/builtin/adapters/memory/sqlite_memory_adapter.py +215 -0
  25. hexdag/builtin/adapters/memory/state_memory.py +280 -0
  26. hexdag/builtin/adapters/mock/README.md +89 -0
  27. hexdag/builtin/adapters/mock/__init__.py +15 -0
  28. hexdag/builtin/adapters/mock/hexdag.toml +50 -0
  29. hexdag/builtin/adapters/mock/mock_database.py +225 -0
  30. hexdag/builtin/adapters/mock/mock_embedding.py +223 -0
  31. hexdag/builtin/adapters/mock/mock_llm.py +177 -0
  32. hexdag/builtin/adapters/mock/mock_tool_adapter.py +192 -0
  33. hexdag/builtin/adapters/mock/mock_tool_router.py +232 -0
  34. hexdag/builtin/adapters/openai/__init__.py +5 -0
  35. hexdag/builtin/adapters/openai/openai_adapter.py +634 -0
  36. hexdag/builtin/adapters/secret/__init__.py +7 -0
  37. hexdag/builtin/adapters/secret/local_secret_adapter.py +248 -0
  38. hexdag/builtin/adapters/unified_tool_router.py +280 -0
  39. hexdag/builtin/macros/__init__.py +17 -0
  40. hexdag/builtin/macros/conversation_agent.py +390 -0
  41. hexdag/builtin/macros/llm_macro.py +151 -0
  42. hexdag/builtin/macros/reasoning_agent.py +423 -0
  43. hexdag/builtin/macros/tool_macro.py +380 -0
  44. hexdag/builtin/nodes/__init__.py +38 -0
  45. hexdag/builtin/nodes/_discovery.py +123 -0
  46. hexdag/builtin/nodes/agent_node.py +696 -0
  47. hexdag/builtin/nodes/base_node_factory.py +242 -0
  48. hexdag/builtin/nodes/composite_node.py +926 -0
  49. hexdag/builtin/nodes/data_node.py +201 -0
  50. hexdag/builtin/nodes/expression_node.py +487 -0
  51. hexdag/builtin/nodes/function_node.py +454 -0
  52. hexdag/builtin/nodes/llm_node.py +491 -0
  53. hexdag/builtin/nodes/loop_node.py +920 -0
  54. hexdag/builtin/nodes/mapped_input.py +518 -0
  55. hexdag/builtin/nodes/port_call_node.py +269 -0
  56. hexdag/builtin/nodes/tool_call_node.py +195 -0
  57. hexdag/builtin/nodes/tool_utils.py +390 -0
  58. hexdag/builtin/prompts/__init__.py +68 -0
  59. hexdag/builtin/prompts/base.py +422 -0
  60. hexdag/builtin/prompts/chat_prompts.py +303 -0
  61. hexdag/builtin/prompts/error_correction_prompts.py +320 -0
  62. hexdag/builtin/prompts/tool_prompts.py +160 -0
  63. hexdag/builtin/tools/builtin_tools.py +84 -0
  64. hexdag/builtin/tools/database_tools.py +164 -0
  65. hexdag/cli/__init__.py +17 -0
  66. hexdag/cli/__main__.py +7 -0
  67. hexdag/cli/commands/__init__.py +27 -0
  68. hexdag/cli/commands/build_cmd.py +812 -0
  69. hexdag/cli/commands/create_cmd.py +208 -0
  70. hexdag/cli/commands/docs_cmd.py +293 -0
  71. hexdag/cli/commands/generate_types_cmd.py +252 -0
  72. hexdag/cli/commands/init_cmd.py +188 -0
  73. hexdag/cli/commands/pipeline_cmd.py +494 -0
  74. hexdag/cli/commands/plugin_dev_cmd.py +529 -0
  75. hexdag/cli/commands/plugins_cmd.py +441 -0
  76. hexdag/cli/commands/studio_cmd.py +101 -0
  77. hexdag/cli/commands/validate_cmd.py +221 -0
  78. hexdag/cli/main.py +84 -0
  79. hexdag/core/__init__.py +83 -0
  80. hexdag/core/config/__init__.py +20 -0
  81. hexdag/core/config/loader.py +479 -0
  82. hexdag/core/config/models.py +150 -0
  83. hexdag/core/configurable.py +294 -0
  84. hexdag/core/context/__init__.py +37 -0
  85. hexdag/core/context/execution_context.py +378 -0
  86. hexdag/core/docs/__init__.py +26 -0
  87. hexdag/core/docs/extractors.py +678 -0
  88. hexdag/core/docs/generators.py +890 -0
  89. hexdag/core/docs/models.py +120 -0
  90. hexdag/core/domain/__init__.py +10 -0
  91. hexdag/core/domain/dag.py +1225 -0
  92. hexdag/core/exceptions.py +234 -0
  93. hexdag/core/expression_parser.py +569 -0
  94. hexdag/core/logging.py +449 -0
  95. hexdag/core/models/__init__.py +17 -0
  96. hexdag/core/models/base.py +138 -0
  97. hexdag/core/orchestration/__init__.py +46 -0
  98. hexdag/core/orchestration/body_executor.py +481 -0
  99. hexdag/core/orchestration/components/__init__.py +97 -0
  100. hexdag/core/orchestration/components/adapter_lifecycle_manager.py +113 -0
  101. hexdag/core/orchestration/components/checkpoint_manager.py +134 -0
  102. hexdag/core/orchestration/components/execution_coordinator.py +360 -0
  103. hexdag/core/orchestration/components/health_check_manager.py +176 -0
  104. hexdag/core/orchestration/components/input_mapper.py +143 -0
  105. hexdag/core/orchestration/components/lifecycle_manager.py +583 -0
  106. hexdag/core/orchestration/components/node_executor.py +377 -0
  107. hexdag/core/orchestration/components/secret_manager.py +202 -0
  108. hexdag/core/orchestration/components/wave_executor.py +158 -0
  109. hexdag/core/orchestration/constants.py +17 -0
  110. hexdag/core/orchestration/events/README.md +312 -0
  111. hexdag/core/orchestration/events/__init__.py +104 -0
  112. hexdag/core/orchestration/events/batching.py +330 -0
  113. hexdag/core/orchestration/events/decorators.py +139 -0
  114. hexdag/core/orchestration/events/events.py +573 -0
  115. hexdag/core/orchestration/events/observers/__init__.py +30 -0
  116. hexdag/core/orchestration/events/observers/core_observers.py +690 -0
  117. hexdag/core/orchestration/events/observers/models.py +111 -0
  118. hexdag/core/orchestration/events/taxonomy.py +269 -0
  119. hexdag/core/orchestration/hook_context.py +237 -0
  120. hexdag/core/orchestration/hooks.py +437 -0
  121. hexdag/core/orchestration/models.py +418 -0
  122. hexdag/core/orchestration/orchestrator.py +910 -0
  123. hexdag/core/orchestration/orchestrator_factory.py +275 -0
  124. hexdag/core/orchestration/port_wrappers.py +327 -0
  125. hexdag/core/orchestration/prompt/__init__.py +32 -0
  126. hexdag/core/orchestration/prompt/template.py +332 -0
  127. hexdag/core/pipeline_builder/__init__.py +21 -0
  128. hexdag/core/pipeline_builder/component_instantiator.py +386 -0
  129. hexdag/core/pipeline_builder/include_tag.py +265 -0
  130. hexdag/core/pipeline_builder/pipeline_config.py +133 -0
  131. hexdag/core/pipeline_builder/py_tag.py +223 -0
  132. hexdag/core/pipeline_builder/tag_discovery.py +268 -0
  133. hexdag/core/pipeline_builder/yaml_builder.py +1196 -0
  134. hexdag/core/pipeline_builder/yaml_validator.py +569 -0
  135. hexdag/core/ports/__init__.py +65 -0
  136. hexdag/core/ports/api_call.py +133 -0
  137. hexdag/core/ports/database.py +489 -0
  138. hexdag/core/ports/embedding.py +215 -0
  139. hexdag/core/ports/executor.py +237 -0
  140. hexdag/core/ports/file_storage.py +117 -0
  141. hexdag/core/ports/healthcheck.py +87 -0
  142. hexdag/core/ports/llm.py +551 -0
  143. hexdag/core/ports/memory.py +70 -0
  144. hexdag/core/ports/observer_manager.py +130 -0
  145. hexdag/core/ports/secret.py +145 -0
  146. hexdag/core/ports/tool_router.py +94 -0
  147. hexdag/core/ports_builder.py +623 -0
  148. hexdag/core/protocols.py +273 -0
  149. hexdag/core/resolver.py +304 -0
  150. hexdag/core/schema/__init__.py +9 -0
  151. hexdag/core/schema/generator.py +742 -0
  152. hexdag/core/secrets.py +242 -0
  153. hexdag/core/types.py +413 -0
  154. hexdag/core/utils/async_warnings.py +206 -0
  155. hexdag/core/utils/schema_conversion.py +78 -0
  156. hexdag/core/utils/sql_validation.py +86 -0
  157. hexdag/core/validation/secure_json.py +148 -0
  158. hexdag/core/yaml_macro.py +517 -0
  159. hexdag/mcp_server.py +3120 -0
  160. hexdag/studio/__init__.py +10 -0
  161. hexdag/studio/build_ui.py +92 -0
  162. hexdag/studio/server/__init__.py +1 -0
  163. hexdag/studio/server/main.py +100 -0
  164. hexdag/studio/server/routes/__init__.py +9 -0
  165. hexdag/studio/server/routes/execute.py +208 -0
  166. hexdag/studio/server/routes/export.py +558 -0
  167. hexdag/studio/server/routes/files.py +207 -0
  168. hexdag/studio/server/routes/plugins.py +419 -0
  169. hexdag/studio/server/routes/validate.py +220 -0
  170. hexdag/studio/ui/index.html +13 -0
  171. hexdag/studio/ui/package-lock.json +2992 -0
  172. hexdag/studio/ui/package.json +31 -0
  173. hexdag/studio/ui/postcss.config.js +6 -0
  174. hexdag/studio/ui/public/hexdag.svg +5 -0
  175. hexdag/studio/ui/src/App.tsx +251 -0
  176. hexdag/studio/ui/src/components/Canvas.tsx +408 -0
  177. hexdag/studio/ui/src/components/ContextMenu.tsx +187 -0
  178. hexdag/studio/ui/src/components/FileBrowser.tsx +123 -0
  179. hexdag/studio/ui/src/components/Header.tsx +181 -0
  180. hexdag/studio/ui/src/components/HexdagNode.tsx +193 -0
  181. hexdag/studio/ui/src/components/NodeInspector.tsx +512 -0
  182. hexdag/studio/ui/src/components/NodePalette.tsx +262 -0
  183. hexdag/studio/ui/src/components/NodePortsSection.tsx +403 -0
  184. hexdag/studio/ui/src/components/PluginManager.tsx +347 -0
  185. hexdag/studio/ui/src/components/PortsEditor.tsx +481 -0
  186. hexdag/studio/ui/src/components/PythonEditor.tsx +195 -0
  187. hexdag/studio/ui/src/components/ValidationPanel.tsx +105 -0
  188. hexdag/studio/ui/src/components/YamlEditor.tsx +196 -0
  189. hexdag/studio/ui/src/components/index.ts +8 -0
  190. hexdag/studio/ui/src/index.css +92 -0
  191. hexdag/studio/ui/src/main.tsx +10 -0
  192. hexdag/studio/ui/src/types/index.ts +123 -0
  193. hexdag/studio/ui/src/vite-env.d.ts +1 -0
  194. hexdag/studio/ui/tailwind.config.js +29 -0
  195. hexdag/studio/ui/tsconfig.json +37 -0
  196. hexdag/studio/ui/tsconfig.node.json +13 -0
  197. hexdag/studio/ui/vite.config.ts +35 -0
  198. hexdag/visualization/__init__.py +69 -0
  199. hexdag/visualization/dag_visualizer.py +1020 -0
  200. hexdag-0.5.0.dev1.dist-info/METADATA +369 -0
  201. hexdag-0.5.0.dev1.dist-info/RECORD +261 -0
  202. hexdag-0.5.0.dev1.dist-info/WHEEL +4 -0
  203. hexdag-0.5.0.dev1.dist-info/entry_points.txt +4 -0
  204. hexdag-0.5.0.dev1.dist-info/licenses/LICENSE +190 -0
  205. hexdag_plugins/.gitignore +43 -0
  206. hexdag_plugins/README.md +73 -0
  207. hexdag_plugins/__init__.py +1 -0
  208. hexdag_plugins/azure/LICENSE +21 -0
  209. hexdag_plugins/azure/README.md +414 -0
  210. hexdag_plugins/azure/__init__.py +21 -0
  211. hexdag_plugins/azure/azure_blob_adapter.py +450 -0
  212. hexdag_plugins/azure/azure_cosmos_adapter.py +383 -0
  213. hexdag_plugins/azure/azure_keyvault_adapter.py +314 -0
  214. hexdag_plugins/azure/azure_openai_adapter.py +415 -0
  215. hexdag_plugins/azure/pyproject.toml +107 -0
  216. hexdag_plugins/azure/tests/__init__.py +1 -0
  217. hexdag_plugins/azure/tests/test_azure_blob_adapter.py +350 -0
  218. hexdag_plugins/azure/tests/test_azure_cosmos_adapter.py +323 -0
  219. hexdag_plugins/azure/tests/test_azure_keyvault_adapter.py +330 -0
  220. hexdag_plugins/azure/tests/test_azure_openai_adapter.py +329 -0
  221. hexdag_plugins/hexdag_etl/README.md +168 -0
  222. hexdag_plugins/hexdag_etl/__init__.py +53 -0
  223. hexdag_plugins/hexdag_etl/examples/01_simple_pandas_transform.py +270 -0
  224. hexdag_plugins/hexdag_etl/examples/02_simple_pandas_only.py +149 -0
  225. hexdag_plugins/hexdag_etl/examples/03_file_io_pipeline.py +109 -0
  226. hexdag_plugins/hexdag_etl/examples/test_pandas_transform.py +84 -0
  227. hexdag_plugins/hexdag_etl/hexdag.toml +25 -0
  228. hexdag_plugins/hexdag_etl/hexdag_etl/__init__.py +48 -0
  229. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/__init__.py +13 -0
  230. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/api_extract.py +230 -0
  231. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/base_node_factory.py +181 -0
  232. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/file_io.py +415 -0
  233. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/outlook.py +492 -0
  234. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/pandas_transform.py +563 -0
  235. hexdag_plugins/hexdag_etl/hexdag_etl/nodes/sql_extract_load.py +112 -0
  236. hexdag_plugins/hexdag_etl/pyproject.toml +82 -0
  237. hexdag_plugins/hexdag_etl/test_transform.py +54 -0
  238. hexdag_plugins/hexdag_etl/tests/test_plugin_integration.py +62 -0
  239. hexdag_plugins/mysql_adapter/LICENSE +21 -0
  240. hexdag_plugins/mysql_adapter/README.md +224 -0
  241. hexdag_plugins/mysql_adapter/__init__.py +6 -0
  242. hexdag_plugins/mysql_adapter/mysql_adapter.py +408 -0
  243. hexdag_plugins/mysql_adapter/pyproject.toml +93 -0
  244. hexdag_plugins/mysql_adapter/tests/test_mysql_adapter.py +259 -0
  245. hexdag_plugins/storage/README.md +184 -0
  246. hexdag_plugins/storage/__init__.py +19 -0
  247. hexdag_plugins/storage/file/__init__.py +5 -0
  248. hexdag_plugins/storage/file/local.py +325 -0
  249. hexdag_plugins/storage/ports/__init__.py +5 -0
  250. hexdag_plugins/storage/ports/vector_store.py +236 -0
  251. hexdag_plugins/storage/sql/__init__.py +7 -0
  252. hexdag_plugins/storage/sql/base.py +187 -0
  253. hexdag_plugins/storage/sql/mysql.py +27 -0
  254. hexdag_plugins/storage/sql/postgresql.py +27 -0
  255. hexdag_plugins/storage/tests/__init__.py +1 -0
  256. hexdag_plugins/storage/tests/test_local_file_storage.py +161 -0
  257. hexdag_plugins/storage/tests/test_sql_adapters.py +212 -0
  258. hexdag_plugins/storage/vector/__init__.py +7 -0
  259. hexdag_plugins/storage/vector/chromadb.py +223 -0
  260. hexdag_plugins/storage/vector/in_memory.py +285 -0
  261. hexdag_plugins/storage/vector/pgvector.py +502 -0
@@ -0,0 +1,332 @@
1
+ """Simple and secure prompt template system for hexdag.
2
+
3
+ This module provides a lightweight template engine that handles variable substitution and nested
4
+ object access without the security risks of full template engines like Jinja2.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from functools import lru_cache
11
+ from typing import Any
12
+
13
+
14
+ class PromptTemplateError(Exception):
15
+ """Base exception for prompt template errors."""
16
+
17
+
18
+ class MissingVariableError(PromptTemplateError):
19
+ """Raised when required template variables are missing."""
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Cached Template Parsing
24
+ # ---------------------------------------------------------------------------
25
+
26
+
27
+ @lru_cache(maxsize=256)
28
+ def _extract_variables_cached(template: str) -> tuple[str, ...]:
29
+ """Extract variable names from template with caching.
30
+
31
+ This function is cached because template parsing with regex is expensive
32
+ and templates are often reused (e.g., same prompt template for multiple nodes).
33
+
34
+ Parameters
35
+ ----------
36
+ template : str
37
+ Template string to analyze
38
+
39
+ Returns
40
+ -------
41
+ tuple[str, ...]
42
+ Tuple of unique root variable names found in template
43
+ """
44
+ pattern = r"\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*\}\}"
45
+ matches = re.findall(pattern, template)
46
+
47
+ root_vars_set = set()
48
+ for match in matches:
49
+ root_var = match.split(".")[0]
50
+ root_vars_set.add(root_var)
51
+
52
+ return tuple(sorted(root_vars_set))
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # Simple Template Engine
57
+ # ---------------------------------------------------------------------------
58
+
59
+
60
+ class PromptTemplate:
61
+ """Simple and secure prompt template with variable substitution.
62
+
63
+ Supports:
64
+ - Basic substitution: {{variable}}
65
+ - Nested access: {{user.name}}
66
+ - Safe variable extraction and validation
67
+
68
+ Does NOT support (for security):
69
+ - Code execution
70
+ - Loops or conditionals
71
+ - Complex expressions
72
+ """
73
+
74
+ def __init__(self, template: str, input_vars: list[str] | None = None) -> None:
75
+ r"""Initialize a PromptTemplate with a template string and optional input variables.
76
+
77
+ Args
78
+ ----
79
+ template: Template string with {{variable}} placeholders
80
+ input_vars: Optional list of variable names. If None, variables are auto-extracted
81
+
82
+ Raises
83
+ ------
84
+ PromptTemplateError: If template has invalid syntax
85
+
86
+ Examples
87
+ --------
88
+ # Auto-extract variables
89
+ template = PromptTemplate("Hello {{name}}!")
90
+ assert template.input_vars == ["name"]
91
+
92
+ # Manually specify variables
93
+ template = PromptTemplate(
94
+ "User: {{user.name}} ({{user.role}})\nMessage: {{message}}",
95
+ ["user", "message"]
96
+ )
97
+ """
98
+ self.template = template
99
+
100
+ if input_vars is not None:
101
+ self.input_vars = list(input_vars)
102
+ else:
103
+ # Use cached extraction for performance
104
+ self.input_vars = list(_extract_variables_cached(template))
105
+
106
+ def _get_nested_value(self, data: dict[str, Any], key: str) -> Any:
107
+ """Get value from nested dictionary using dot notation.
108
+
109
+ Args
110
+ ----
111
+ data: Dictionary to extract value from
112
+ key: Key path (e.g., "user.name" -> data["user"]["name"])
113
+
114
+ Returns
115
+ -------
116
+ Any
117
+ The value at the specified path
118
+ """
119
+ parts = key.split(".")
120
+ value = data
121
+ for part in parts:
122
+ value = value[part] if isinstance(value, dict) else getattr(value, part)
123
+ return value
124
+
125
+ def render(self, **kwargs: Any) -> str:
126
+ """Render the template with provided variables.
127
+
128
+ Args
129
+ ----
130
+ **kwargs: Variable values to substitute in template
131
+
132
+ Returns
133
+ -------
134
+ str
135
+ Rendered template string
136
+
137
+ Raises
138
+ ------
139
+ MissingVariableError
140
+ If required variables are missing
141
+
142
+ Examples
143
+ --------
144
+ template = PromptTemplate("Hello {{name}}!")
145
+ result = template.render(name="Alice") # "Hello Alice!"
146
+
147
+ # With nested data
148
+ template = PromptTemplate("User: {{user.name}}")
149
+ result = template.render(user={"name": "Bob"}) # "User: Bob"
150
+ """
151
+ # Check for missing required variables
152
+ missing_vars = [var for var in self.input_vars if var not in kwargs]
153
+
154
+ if missing_vars:
155
+ raise MissingVariableError(
156
+ f"Missing required template variables: {missing_vars}. "
157
+ f"Required variables: {self.input_vars}. "
158
+ f"Provided variables: {list(kwargs.keys())}"
159
+ )
160
+
161
+ # Perform variable substitution
162
+ result = self.template
163
+
164
+ # Find all variable references in the template
165
+ pattern = r"\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*\}\}"
166
+
167
+ def replace_var(match: Any) -> str:
168
+ var_path = match.group(1).strip()
169
+ try:
170
+ value = self._get_nested_value(kwargs, var_path)
171
+ return str(value)
172
+ except (KeyError, AttributeError) as e:
173
+ raise MissingVariableError(f"Cannot access variable '{var_path}': {e}") from e
174
+
175
+ return re.sub(pattern, replace_var, result)
176
+
177
+ def parse_output(self, output: str) -> str:
178
+ r"""Parse and post-process rendered template output.
179
+
180
+ Default implementation is a passthrough that returns the output unchanged.
181
+ Override this method in subclasses for custom output processing.
182
+
183
+ Args
184
+ ----
185
+ output: Rendered template string
186
+
187
+ Returns
188
+ -------
189
+ str
190
+ Processed output string (by default, unchanged)
191
+
192
+ Examples
193
+ --------
194
+ # Default behavior
195
+ template = PromptTemplate("Hello {{name}}!")
196
+ rendered = template.render(name="Alice")
197
+ parsed = template.parse_output(rendered) # Same as rendered
198
+
199
+ # Custom parsing in subclass
200
+ class UppercaseTemplate(PromptTemplate):
201
+ def parse_output(self, output: str) -> str:
202
+ return output.upper()
203
+ """
204
+ return output
205
+
206
+ def format(self, **kwargs: Any) -> str:
207
+ r"""Render template and parse output in one step.
208
+
209
+ Args
210
+ ----
211
+ **kwargs: Variable values to substitute in template
212
+
213
+ Returns
214
+ -------
215
+ str
216
+ Rendered and parsed template string
217
+
218
+ Examples
219
+ --------
220
+ template = PromptTemplate("Hello {{name}}!")
221
+ result = template.format(name="Alice") # "Hello Alice!"
222
+ """
223
+ rendered = self.render(**kwargs)
224
+ return self.parse_output(rendered)
225
+
226
+ def to_messages(self, system_prompt: str | None = None, **kwargs: Any) -> list[dict[str, str]]:
227
+ r"""Convert template to role-based messages for LLMFactory.
228
+
229
+ Args
230
+ ----
231
+ system_prompt: Optional system prompt to include
232
+ **kwargs: Variable values to substitute in template
233
+
234
+ Returns
235
+ -------
236
+ list[dict[str, str]]
237
+ List of message dictionaries with 'role' and 'content' keys
238
+
239
+ Examples
240
+ --------
241
+ template = PromptTemplate("Analyze this data: {{data}}")
242
+
243
+ # With system prompt
244
+ messages = template.to_messages(
245
+ system_prompt="You are an expert analyst.",
246
+ data="Q4 sales data"
247
+ )
248
+ # Returns: [
249
+ # {"role": "system", "content": "You are an expert analyst."},
250
+ # {"role": "user", "content": "Analyze this data: Q4 sales data"}
251
+ # ]
252
+
253
+ # Without system prompt
254
+ messages = template.to_messages(data="Q4 sales data")
255
+ # Returns: [{"role": "user", "content": "Analyze this data: Q4 sales data"}]
256
+ """
257
+ messages = []
258
+
259
+ if system_prompt:
260
+ messages.append({"role": "system", "content": system_prompt})
261
+
262
+ user_content = self.format(**kwargs)
263
+ messages.append({"role": "user", "content": user_content})
264
+
265
+ return messages
266
+
267
+ def __str__(self) -> str:
268
+ """Return string representation showing template and variables.
269
+
270
+ Returns
271
+ -------
272
+ str
273
+ String representation of the template
274
+ """
275
+ return f"PromptTemplate(vars={self.input_vars})"
276
+
277
+ def __repr__(self) -> str:
278
+ """Return detailed representation for debugging.
279
+
280
+ Returns
281
+ -------
282
+ str
283
+ Detailed string representation for debugging
284
+ """
285
+ return f"PromptTemplate(template='{self.template[:50]}...', input_vars={self.input_vars})"
286
+
287
+ def __add__(self, other: str | PromptTemplate) -> PromptTemplate:
288
+ """Add text or another template using + operator (builder pattern).
289
+
290
+ Args
291
+ ----
292
+ other: Text string or PromptTemplate to append
293
+
294
+ Returns
295
+ -------
296
+ PromptTemplate
297
+ New PromptTemplate instance with combined content and variables
298
+
299
+ Examples
300
+ --------
301
+ # String composition
302
+ template = PromptTemplate("Hello {{name}}") + "!"
303
+
304
+ # Template composition (builder pattern)
305
+ base = PromptTemplate("Analyze {{data}}")
306
+ tools = PromptTemplate("\\nTools: {{tools}}")
307
+ composed = base + tools # Merges templates and variables
308
+ """
309
+ if isinstance(other, PromptTemplate):
310
+ # Compose two templates - merge content and variables
311
+ enhanced_template = self.template + other.template
312
+ # Merge input variables from both templates
313
+ merged_vars = list(dict.fromkeys(self.input_vars + other.input_vars))
314
+ return PromptTemplate(enhanced_template, merged_vars)
315
+
316
+ # String composition
317
+ enhanced_template = self.template + other
318
+ return PromptTemplate(enhanced_template, self.input_vars)
319
+
320
+ def add(self, text: str | PromptTemplate) -> PromptTemplate:
321
+ """Add text or template to template (alias for + operator).
322
+
323
+ Args
324
+ ----
325
+ text: Text string or PromptTemplate to append
326
+
327
+ Returns
328
+ -------
329
+ PromptTemplate
330
+ New PromptTemplate instance with enhanced content
331
+ """
332
+ return self + text
@@ -0,0 +1,21 @@
1
+ """Pipeline modules for hexDAG framework.
2
+
3
+ This package contains the YAML workflow builder for creating DirectedGraphs
4
+ from declarative YAML configurations.
5
+
6
+ """
7
+
8
+ # Import tags to register YAML custom tags
9
+ from . import include_tag as _include_tag # noqa: F401
10
+ from . import py_tag as _py_tag # noqa: F401
11
+ from .include_tag import set_include_base_path
12
+ from .tag_discovery import discover_tags, get_known_tag_names, get_tag_schema
13
+ from .yaml_builder import YamlPipelineBuilder
14
+
15
+ __all__ = [
16
+ "YamlPipelineBuilder",
17
+ "set_include_base_path",
18
+ "discover_tags",
19
+ "get_known_tag_names",
20
+ "get_tag_schema",
21
+ ]