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,303 @@
1
+ """Registrable chat and few-shot prompt templates.
2
+
3
+ These are pre-configured, reusable prompt templates that can be
4
+ referenced in YAML pipelines or composed programmatically.
5
+ """
6
+
7
+ from collections.abc import Callable
8
+ from typing import Any
9
+
10
+ from hexdag.builtin.prompts.base import (
11
+ ChatFewShotTemplate,
12
+ ChatPromptTemplate,
13
+ FewShotPromptTemplate,
14
+ )
15
+
16
+
17
+ class ChatQAPrompt(ChatPromptTemplate):
18
+ """Chat template for question-answering tasks.
19
+
20
+ Provides a helpful assistant persona with structured Q&A format.
21
+
22
+ Examples
23
+ --------
24
+ Direct usage::
25
+
26
+ prompt = ChatQAPrompt()
27
+ messages = prompt.to_messages(
28
+ domain="AI",
29
+ question="What is machine learning?"
30
+ )
31
+
32
+ Composition::
33
+
34
+ from hexdag.builtin.prompts import ToolPrompt
35
+ full_prompt = ChatQAPrompt() + ToolPrompt()
36
+
37
+ YAML usage::
38
+
39
+ nodes:
40
+ - type: prompt_node
41
+ name: qa_prompt
42
+ params:
43
+ template: core:chat_qa
44
+ inputs:
45
+ domain: "science"
46
+ question: "{{user_question}}"
47
+ """
48
+
49
+ def __init__(self) -> None:
50
+ """Initialize Q&A chat template."""
51
+ super().__init__(
52
+ system_message="You are a helpful expert in {{domain}}. "
53
+ "Provide clear, accurate, and concise answers.",
54
+ human_message="Question: {{question}}",
55
+ )
56
+
57
+
58
+ class ChatAnalysisPrompt(ChatPromptTemplate):
59
+ """Chat template for analytical tasks.
60
+
61
+ Encourages step-by-step reasoning and structured output.
62
+
63
+ Examples
64
+ --------
65
+ prompt = ChatAnalysisPrompt()
66
+ messages = prompt.to_messages(
67
+ task="sentiment analysis",
68
+ data="Customer feedback: Great product!"
69
+ )
70
+ """
71
+
72
+ def __init__(self) -> None:
73
+ """Initialize analysis chat template."""
74
+ super().__init__(
75
+ system_message="You are an expert analyst. "
76
+ "Analyze the given data thoroughly and provide structured insights. "
77
+ "Think step-by-step and explain your reasoning.",
78
+ human_message="Task: {{task}}\n\nData:\n{{data}}\n\nProvide your analysis:",
79
+ )
80
+
81
+
82
+ class ChatConversationalPrompt(ChatPromptTemplate):
83
+ """Conversational chat template with conversation history.
84
+
85
+ Designed for multi-turn conversations with context.
86
+
87
+ Examples
88
+ --------
89
+ prompt = ChatConversationalPrompt()
90
+ messages = prompt.to_messages(
91
+ bot_name="Assistant",
92
+ user_message="Tell me about quantum computing",
93
+ context_history=[
94
+ {"role": "user", "content": "Hello!"},
95
+ {"role": "assistant", "content": "Hi! How can I help?"}
96
+ ]
97
+ )
98
+ """
99
+
100
+ def __init__(self) -> None:
101
+ """Initialize conversational chat template."""
102
+ super().__init__(
103
+ system_message="You are {{bot_name}}, a friendly and helpful conversational AI. "
104
+ "Maintain context from previous messages and provide engaging responses.",
105
+ human_message="{{user_message}}",
106
+ )
107
+
108
+
109
+ class FewShotClassificationPrompt(FewShotPromptTemplate):
110
+ """Few-shot template for classification tasks.
111
+
112
+ Provides examples to guide the model's classification behavior.
113
+
114
+ Examples
115
+ --------
116
+ Direct usage::
117
+
118
+ examples = [
119
+ {"input": "I love this!", "output": "positive"},
120
+ {"input": "Terrible experience", "output": "negative"},
121
+ {"input": "It's okay", "output": "neutral"}
122
+ ]
123
+
124
+ prompt = FewShotClassificationPrompt(examples=examples)
125
+ text = prompt.format(text="Amazing product!")
126
+
127
+ YAML usage::
128
+
129
+ nodes:
130
+ - type: prompt_node
131
+ name: classifier
132
+ params:
133
+ template: core:fewshot_classification
134
+ examples:
135
+ - input: "Great!"
136
+ output: "positive"
137
+ - input: "Bad"
138
+ output: "negative"
139
+ inputs:
140
+ text: "{{review}}"
141
+ """
142
+
143
+ def __init__(
144
+ self,
145
+ examples: list[dict[str, Any]] | None = None,
146
+ format_example: Callable[[dict[str, Any]], str] | None = None,
147
+ ) -> None:
148
+ """Initialize classification few-shot template.
149
+
150
+ Args
151
+ ----
152
+ examples: List of example dicts with 'input' and 'output' keys
153
+ format_example: Optional custom formatter for examples
154
+ """
155
+ if format_example is None:
156
+
157
+ def _classification_formatter(ex: dict[str, Any]) -> str:
158
+ inp = ex.get("input", "")
159
+ out = ex.get("output", "")
160
+ return f"Text: {inp}\nClassification: {out}"
161
+
162
+ format_example = _classification_formatter
163
+
164
+ super().__init__(
165
+ template="Classify the following text:\n\nText: {{text}}\nClassification:",
166
+ examples=examples or [],
167
+ format_example=format_example,
168
+ example_separator="\n\n",
169
+ )
170
+
171
+
172
+ class FewShotExtractionPrompt(FewShotPromptTemplate):
173
+ """Few-shot template for extracting structured information.
174
+
175
+ Examples
176
+ --------
177
+ examples = [
178
+ {
179
+ "input": "John Doe, age 30, lives in NYC",
180
+ "output": '{"name": "John Doe", "age": 30, "city": "NYC"}'
181
+ }
182
+ ]
183
+
184
+ prompt = FewShotExtractionPrompt(examples=examples)
185
+ text = prompt.format(text="Jane Smith, 25, from LA")
186
+ """
187
+
188
+ def __init__(
189
+ self,
190
+ examples: list[dict[str, Any]] | None = None,
191
+ format_example: Callable[[dict[str, Any]], str] | None = None,
192
+ ) -> None:
193
+ """Initialize extraction few-shot template."""
194
+ if format_example is None:
195
+
196
+ def _extraction_formatter(ex: dict[str, Any]) -> str:
197
+ inp = ex.get("input", "")
198
+ out = ex.get("output", "")
199
+ return f"Input: {inp}\nExtracted: {out}"
200
+
201
+ format_example = _extraction_formatter
202
+
203
+ super().__init__(
204
+ template="Extract structured information from the following text:\n\nInput: {{text}}\nExtracted:",
205
+ examples=examples or [],
206
+ format_example=format_example,
207
+ example_separator="\n\n",
208
+ )
209
+
210
+
211
+ class ChatFewShotQAPrompt(ChatFewShotTemplate):
212
+ """Chat template with few-shot examples for question answering.
213
+
214
+ Combines chat-style interaction with example-based learning.
215
+
216
+ Examples
217
+ --------
218
+ examples = [
219
+ {
220
+ "input": "What is Python?",
221
+ "output": "Python is a high-level programming language known for simplicity."
222
+ },
223
+ {
224
+ "input": "What is AI?",
225
+ "output": "AI is the simulation of human intelligence by machines."
226
+ }
227
+ ]
228
+
229
+ prompt = ChatFewShotQAPrompt(examples=examples)
230
+ messages = prompt.to_messages(question="What is blockchain?")
231
+ """
232
+
233
+ def __init__(
234
+ self,
235
+ examples: list[dict[str, Any]] | None = None,
236
+ format_example: Callable[[dict[str, Any]], str] | None = None,
237
+ ) -> None:
238
+ """Initialize chat few-shot Q&A template."""
239
+ if format_example is None:
240
+
241
+ def _qa_formatter(ex: dict[str, Any]) -> str:
242
+ inp = ex.get("input", "")
243
+ out = ex.get("output", "")
244
+ return f"Q: {inp}\nA: {out}"
245
+
246
+ format_example = _qa_formatter
247
+
248
+ super().__init__(
249
+ system_message="You are a knowledgeable assistant. "
250
+ "Answer questions clearly and concisely, following the example format.",
251
+ human_message="Q: {{question}}\nA:",
252
+ examples=examples or [],
253
+ format_example=format_example,
254
+ example_separator="\n\n",
255
+ )
256
+
257
+
258
+ # Factory function for creating custom chat prompts
259
+ def create_chat_prompt(
260
+ system_message: str,
261
+ human_message: str,
262
+ ) -> type[ChatPromptTemplate]:
263
+ """Factory for creating custom chat prompts.
264
+
265
+ Examples
266
+ --------
267
+ MyCustomPrompt = create_chat_prompt(
268
+ system_message="You are a {{role}}",
269
+ human_message="{{task}}",
270
+ )
271
+ """
272
+
273
+ class CustomChatPrompt(ChatPromptTemplate):
274
+ def __init__(self) -> None:
275
+ super().__init__(system_message=system_message, human_message=human_message)
276
+
277
+ return CustomChatPrompt
278
+
279
+
280
+ # Factory for creating custom few-shot prompts
281
+ def create_fewshot_prompt(
282
+ template: str,
283
+ examples: list[dict[str, Any]],
284
+ format_example: Callable[[dict[str, Any]], str] | None = None,
285
+ ) -> type[FewShotPromptTemplate]:
286
+ """Factory for creating custom few-shot prompts.
287
+
288
+ Examples
289
+ --------
290
+ MyFewShotPrompt = create_fewshot_prompt(
291
+ template="Translate: {{text}}",
292
+ examples=[
293
+ {"input": "Hello", "output": "Hola"},
294
+ {"input": "Goodbye", "output": "Adiós"}
295
+ ],
296
+ )
297
+ """
298
+
299
+ class CustomFewShotPrompt(FewShotPromptTemplate):
300
+ def __init__(self) -> None:
301
+ super().__init__(template=template, examples=examples, format_example=format_example)
302
+
303
+ return CustomFewShotPrompt
@@ -0,0 +1,320 @@
1
+ """Error correction and retry prompt templates.
2
+
3
+ These prompts help LLMs fix their mistakes when parsing fails.
4
+ They provide clear instructions on what went wrong and how to fix it.
5
+ """
6
+
7
+ from hexdag.core.orchestration.prompt.template import PromptTemplate
8
+
9
+
10
+ class JsonParseErrorPrompt(PromptTemplate):
11
+ """Prompt to help fix JSON parsing errors.
12
+
13
+ Uses clear examples and common mistake patterns to guide the LLM
14
+ toward producing valid JSON.
15
+
16
+ Variables
17
+ ---------
18
+ - original_prompt: The original prompt that was given
19
+ - llm_output: The output that failed to parse
20
+ - error_message: The parse error message
21
+ - schema: Expected JSON schema (optional)
22
+
23
+ Examples
24
+ --------
25
+ Usage in ParseRetryPolicy::
26
+
27
+ retry_prompt = JsonParseErrorPrompt()
28
+ corrected = retry_prompt.render(
29
+ original_prompt="Generate user data",
30
+ llm_output='name: "John", age: 30', # Invalid JSON
31
+ error_message="Expecting property name enclosed in double quotes",
32
+ schema={"name": "str", "age": "int"}
33
+ )
34
+ """
35
+
36
+ def __init__(self) -> None:
37
+ """Initialize JSON parse error correction prompt."""
38
+ template = """{{original_prompt}}
39
+
40
+ ⚠️ PREVIOUS ATTEMPT FAILED - JSON PARSING ERROR
41
+
42
+ Your previous response could not be parsed as valid JSON:
43
+ ```
44
+ {{llm_output}}
45
+ ```
46
+
47
+ Error: {{error_message}}
48
+
49
+ IMPORTANT: Respond with VALID JSON that follows these rules:
50
+
51
+ 1. ✅ Use double quotes for strings: "text" not 'text'
52
+ 2. ✅ No trailing commas: {"a": 1, "b": 2} not {"a": 1, "b": 2,}
53
+ 3. ✅ Property names must be quoted: {"name": "value"} not {name: "value"}
54
+ 4. ✅ Use true/false not True/False
55
+ 5. ✅ Use null not None
56
+
57
+ Common mistakes to avoid:
58
+ - ❌ Single quotes: {'name': 'John'}
59
+ - ❌ Trailing commas: {"age": 30,}
60
+ - ❌ Unquoted keys: {name: "John"}
61
+ - ❌ Python booleans: {"active": True}
62
+
63
+ Correct format:
64
+ - ✅ {"name": "John", "age": 30, "active": true}
65
+
66
+ {{#schema}}Expected schema:
67
+ ```json
68
+ {{schema}}
69
+ ```{{/schema}}
70
+
71
+ Please respond ONLY with valid JSON. No explanations, no markdown formatting.
72
+ """
73
+ super().__init__(template)
74
+
75
+
76
+ class JsonValidationErrorPrompt(PromptTemplate):
77
+ """Prompt to fix schema validation errors.
78
+
79
+ When JSON parses correctly but doesn't match the expected schema,
80
+ this prompt helps the LLM understand what fields are missing or incorrect.
81
+
82
+ Variables
83
+ ---------
84
+ - original_prompt: The original prompt
85
+ - llm_output: The parsed JSON that failed validation
86
+ - validation_errors: List of validation errors
87
+ - required_fields: Required fields in the schema
88
+ - schema: Full schema specification
89
+
90
+ Examples
91
+ --------
92
+ Usage::
93
+
94
+ retry_prompt = JsonValidationErrorPrompt()
95
+ corrected = retry_prompt.render(
96
+ original_prompt="Generate user profile",
97
+ llm_output='{"name": "John"}', # Missing 'age' field
98
+ validation_errors=["Field 'age' required"],
99
+ required_fields=["name", "age", "email"],
100
+ schema={"name": "str", "age": "int", "email": "str"}
101
+ )
102
+ """
103
+
104
+ def __init__(self) -> None:
105
+ """Initialize JSON validation error correction prompt."""
106
+ template = """{{original_prompt}}
107
+
108
+ ⚠️ PREVIOUS ATTEMPT FAILED - SCHEMA VALIDATION ERROR
109
+
110
+ Your response was valid JSON but did NOT match the required schema.
111
+
112
+ Your output:
113
+ ```json
114
+ {{llm_output}}
115
+ ```
116
+
117
+ Validation errors:
118
+ {{validation_errors}}
119
+
120
+ Required schema:
121
+ ```json
122
+ {{schema}}
123
+ ```
124
+
125
+ REQUIRED FIELDS (must include ALL):
126
+ {{#required_fields}}- {{.}}
127
+ {{/required_fields}}
128
+
129
+ Instructions:
130
+ 1. Include ALL required fields
131
+ 2. Use correct data types (string, number, boolean, array, object)
132
+ 3. Field names must match EXACTLY (case-sensitive)
133
+ 4. Arrays must contain the correct element types
134
+
135
+ Example of CORRECT format:
136
+ ```json
137
+ {
138
+ "name": "John Doe",
139
+ "age": 30,
140
+ "email": "john@example.com",
141
+ "active": true
142
+ }
143
+ ```
144
+
145
+ Please respond with valid JSON that matches the schema exactly.
146
+ """
147
+ super().__init__(template)
148
+
149
+
150
+ class SafeJsonInstructionsPrompt(PromptTemplate):
151
+ """Proactive JSON instructions to prevent common errors.
152
+
153
+ Add this to prompts that expect JSON output to reduce errors upfront.
154
+
155
+ Variables
156
+ ---------
157
+ - schema: Expected JSON schema
158
+
159
+ Examples
160
+ --------
161
+ Compose with main prompt::
162
+
163
+ from hexdag.builtin.prompts import SafeJsonInstructionsPrompt
164
+
165
+ main_prompt = PromptTemplate("Generate user data: {{requirements}}")
166
+ json_instructions = SafeJsonInstructionsPrompt()
167
+
168
+ full_prompt = main_prompt + json_instructions
169
+ """
170
+
171
+ def __init__(self) -> None:
172
+ """Initialize safe JSON instructions prompt."""
173
+ template = """
174
+ ## JSON Output Requirements
175
+
176
+ IMPORTANT: Your response must be valid JSON following these rules:
177
+
178
+ ✅ Valid JSON format:
179
+ - Use double quotes for strings: "text"
180
+ - No trailing commas
181
+ - Quote all property names
182
+ - Use true/false (not True/False)
183
+ - Use null (not None)
184
+
185
+ {{#schema}}Expected structure:
186
+ ```json
187
+ {{schema}}
188
+ ```{{/schema}}
189
+
190
+ Respond with ONLY the JSON object. No explanations before or after.
191
+ """
192
+ super().__init__(template)
193
+
194
+
195
+ class MarkdownJsonErrorPrompt(PromptTemplate):
196
+ """Prompt to fix JSON wrapped in markdown code blocks.
197
+
198
+ Helps when the LLM correctly generates JSON but wraps it in
199
+ markdown formatting that breaks parsing.
200
+
201
+ Variables
202
+ ---------
203
+ - original_prompt: Original prompt
204
+ - error_message: Parse error
205
+
206
+ Examples
207
+ --------
208
+ retry_prompt = MarkdownJsonErrorPrompt()
209
+ corrected = retry_prompt.render(
210
+ original_prompt="Generate config",
211
+ error_message="Could not find JSON in response"
212
+ )
213
+ """
214
+
215
+ def __init__(self) -> None:
216
+ """Initialize markdown JSON error prompt."""
217
+ template = """{{original_prompt}}
218
+
219
+ ⚠️ PREVIOUS ATTEMPT FAILED - JSON FORMATTING ERROR
220
+
221
+ Error: {{error_message}}
222
+
223
+ IMPORTANT: Respond with ONLY the raw JSON object.
224
+
225
+ ❌ DO NOT wrap in markdown code blocks:
226
+ ```json
227
+ {"name": "value"}
228
+ ```
229
+
230
+ ❌ DO NOT add explanations:
231
+ Here is the JSON: {"name": "value"}
232
+
233
+ ✅ CORRECT - Just the JSON:
234
+ {"name": "value"}
235
+
236
+ Please provide ONLY the JSON object, nothing else.
237
+ """
238
+ super().__init__(template)
239
+
240
+
241
+ class GenericParseErrorPrompt(PromptTemplate):
242
+ """Generic parse error correction prompt.
243
+
244
+ Fallback for parse errors that don't fit specific categories.
245
+
246
+ Variables
247
+ ---------
248
+ - original_prompt: Original prompt
249
+ - llm_output: Failed output
250
+ - error_message: Error message
251
+ - format: Expected format (json, yaml, etc.)
252
+
253
+ Examples
254
+ --------
255
+ retry_prompt = GenericParseErrorPrompt()
256
+ corrected = retry_prompt.render(
257
+ original_prompt="Extract data",
258
+ llm_output="invalid data...",
259
+ error_message="Parse failed",
260
+ format="json"
261
+ )
262
+ """
263
+
264
+ def __init__(self) -> None:
265
+ """Initialize generic parse error prompt."""
266
+ template = """{{original_prompt}}
267
+
268
+ ⚠️ PREVIOUS ATTEMPT FAILED
269
+
270
+ Your previous response could not be parsed:
271
+ ```
272
+ {{llm_output}}
273
+ ```
274
+
275
+ Error: {{error_message}}
276
+
277
+ Expected format: {{format}}
278
+
279
+ Please provide a response in valid {{format}} format.
280
+ Ensure:
281
+ - Correct syntax
282
+ - Proper formatting
283
+ - All required fields included
284
+
285
+ Try again with a correctly formatted response.
286
+ """
287
+ super().__init__(template)
288
+
289
+
290
+ def get_error_correction_prompt(error_type: str, strategy: str = "json") -> type[PromptTemplate]:
291
+ """Get appropriate error correction prompt for error type.
292
+
293
+ Args
294
+ ----
295
+ error_type: Type of error ("parse", "validation", "markdown", "generic")
296
+ strategy: Parsing strategy ("json", "yaml", etc.)
297
+
298
+ Returns
299
+ -------
300
+ type[PromptTemplate]
301
+ Appropriate error correction prompt class
302
+
303
+ Examples
304
+ --------
305
+ PromptClass = get_error_correction_prompt("parse", "json")
306
+ prompt = PromptClass()
307
+ corrected = prompt.render(original_prompt="...", error_message="...")
308
+ """
309
+ if error_type == "parse":
310
+ if strategy == "json":
311
+ return JsonParseErrorPrompt
312
+ return GenericParseErrorPrompt
313
+
314
+ if error_type == "validation":
315
+ return JsonValidationErrorPrompt
316
+
317
+ if error_type == "markdown":
318
+ return MarkdownJsonErrorPrompt
319
+
320
+ return GenericParseErrorPrompt