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.
- hexdag/__init__.py +116 -0
- hexdag/__main__.py +30 -0
- hexdag/adapters/executors/__init__.py +5 -0
- hexdag/adapters/executors/local_executor.py +316 -0
- hexdag/builtin/__init__.py +6 -0
- hexdag/builtin/adapters/__init__.py +51 -0
- hexdag/builtin/adapters/anthropic/__init__.py +5 -0
- hexdag/builtin/adapters/anthropic/anthropic_adapter.py +151 -0
- hexdag/builtin/adapters/database/__init__.py +6 -0
- hexdag/builtin/adapters/database/csv/csv_adapter.py +249 -0
- hexdag/builtin/adapters/database/pgvector/__init__.py +5 -0
- hexdag/builtin/adapters/database/pgvector/pgvector_adapter.py +478 -0
- hexdag/builtin/adapters/database/sqlalchemy/sqlalchemy_adapter.py +252 -0
- hexdag/builtin/adapters/database/sqlite/__init__.py +5 -0
- hexdag/builtin/adapters/database/sqlite/sqlite_adapter.py +410 -0
- hexdag/builtin/adapters/local/README.md +59 -0
- hexdag/builtin/adapters/local/__init__.py +7 -0
- hexdag/builtin/adapters/local/local_observer_manager.py +696 -0
- hexdag/builtin/adapters/memory/__init__.py +47 -0
- hexdag/builtin/adapters/memory/file_memory_adapter.py +297 -0
- hexdag/builtin/adapters/memory/in_memory_memory.py +216 -0
- hexdag/builtin/adapters/memory/schemas.py +57 -0
- hexdag/builtin/adapters/memory/session_memory.py +178 -0
- hexdag/builtin/adapters/memory/sqlite_memory_adapter.py +215 -0
- hexdag/builtin/adapters/memory/state_memory.py +280 -0
- hexdag/builtin/adapters/mock/README.md +89 -0
- hexdag/builtin/adapters/mock/__init__.py +15 -0
- hexdag/builtin/adapters/mock/hexdag.toml +50 -0
- hexdag/builtin/adapters/mock/mock_database.py +225 -0
- hexdag/builtin/adapters/mock/mock_embedding.py +223 -0
- hexdag/builtin/adapters/mock/mock_llm.py +177 -0
- hexdag/builtin/adapters/mock/mock_tool_adapter.py +192 -0
- hexdag/builtin/adapters/mock/mock_tool_router.py +232 -0
- hexdag/builtin/adapters/openai/__init__.py +5 -0
- hexdag/builtin/adapters/openai/openai_adapter.py +634 -0
- hexdag/builtin/adapters/secret/__init__.py +7 -0
- hexdag/builtin/adapters/secret/local_secret_adapter.py +248 -0
- hexdag/builtin/adapters/unified_tool_router.py +280 -0
- hexdag/builtin/macros/__init__.py +17 -0
- hexdag/builtin/macros/conversation_agent.py +390 -0
- hexdag/builtin/macros/llm_macro.py +151 -0
- hexdag/builtin/macros/reasoning_agent.py +423 -0
- hexdag/builtin/macros/tool_macro.py +380 -0
- hexdag/builtin/nodes/__init__.py +38 -0
- hexdag/builtin/nodes/_discovery.py +123 -0
- hexdag/builtin/nodes/agent_node.py +696 -0
- hexdag/builtin/nodes/base_node_factory.py +242 -0
- hexdag/builtin/nodes/composite_node.py +926 -0
- hexdag/builtin/nodes/data_node.py +201 -0
- hexdag/builtin/nodes/expression_node.py +487 -0
- hexdag/builtin/nodes/function_node.py +454 -0
- hexdag/builtin/nodes/llm_node.py +491 -0
- hexdag/builtin/nodes/loop_node.py +920 -0
- hexdag/builtin/nodes/mapped_input.py +518 -0
- hexdag/builtin/nodes/port_call_node.py +269 -0
- hexdag/builtin/nodes/tool_call_node.py +195 -0
- hexdag/builtin/nodes/tool_utils.py +390 -0
- hexdag/builtin/prompts/__init__.py +68 -0
- hexdag/builtin/prompts/base.py +422 -0
- hexdag/builtin/prompts/chat_prompts.py +303 -0
- hexdag/builtin/prompts/error_correction_prompts.py +320 -0
- hexdag/builtin/prompts/tool_prompts.py +160 -0
- hexdag/builtin/tools/builtin_tools.py +84 -0
- hexdag/builtin/tools/database_tools.py +164 -0
- hexdag/cli/__init__.py +17 -0
- hexdag/cli/__main__.py +7 -0
- hexdag/cli/commands/__init__.py +27 -0
- hexdag/cli/commands/build_cmd.py +812 -0
- hexdag/cli/commands/create_cmd.py +208 -0
- hexdag/cli/commands/docs_cmd.py +293 -0
- hexdag/cli/commands/generate_types_cmd.py +252 -0
- hexdag/cli/commands/init_cmd.py +188 -0
- hexdag/cli/commands/pipeline_cmd.py +494 -0
- hexdag/cli/commands/plugin_dev_cmd.py +529 -0
- hexdag/cli/commands/plugins_cmd.py +441 -0
- hexdag/cli/commands/studio_cmd.py +101 -0
- hexdag/cli/commands/validate_cmd.py +221 -0
- hexdag/cli/main.py +84 -0
- hexdag/core/__init__.py +83 -0
- hexdag/core/config/__init__.py +20 -0
- hexdag/core/config/loader.py +479 -0
- hexdag/core/config/models.py +150 -0
- hexdag/core/configurable.py +294 -0
- hexdag/core/context/__init__.py +37 -0
- hexdag/core/context/execution_context.py +378 -0
- hexdag/core/docs/__init__.py +26 -0
- hexdag/core/docs/extractors.py +678 -0
- hexdag/core/docs/generators.py +890 -0
- hexdag/core/docs/models.py +120 -0
- hexdag/core/domain/__init__.py +10 -0
- hexdag/core/domain/dag.py +1225 -0
- hexdag/core/exceptions.py +234 -0
- hexdag/core/expression_parser.py +569 -0
- hexdag/core/logging.py +449 -0
- hexdag/core/models/__init__.py +17 -0
- hexdag/core/models/base.py +138 -0
- hexdag/core/orchestration/__init__.py +46 -0
- hexdag/core/orchestration/body_executor.py +481 -0
- hexdag/core/orchestration/components/__init__.py +97 -0
- hexdag/core/orchestration/components/adapter_lifecycle_manager.py +113 -0
- hexdag/core/orchestration/components/checkpoint_manager.py +134 -0
- hexdag/core/orchestration/components/execution_coordinator.py +360 -0
- hexdag/core/orchestration/components/health_check_manager.py +176 -0
- hexdag/core/orchestration/components/input_mapper.py +143 -0
- hexdag/core/orchestration/components/lifecycle_manager.py +583 -0
- hexdag/core/orchestration/components/node_executor.py +377 -0
- hexdag/core/orchestration/components/secret_manager.py +202 -0
- hexdag/core/orchestration/components/wave_executor.py +158 -0
- hexdag/core/orchestration/constants.py +17 -0
- hexdag/core/orchestration/events/README.md +312 -0
- hexdag/core/orchestration/events/__init__.py +104 -0
- hexdag/core/orchestration/events/batching.py +330 -0
- hexdag/core/orchestration/events/decorators.py +139 -0
- hexdag/core/orchestration/events/events.py +573 -0
- hexdag/core/orchestration/events/observers/__init__.py +30 -0
- hexdag/core/orchestration/events/observers/core_observers.py +690 -0
- hexdag/core/orchestration/events/observers/models.py +111 -0
- hexdag/core/orchestration/events/taxonomy.py +269 -0
- hexdag/core/orchestration/hook_context.py +237 -0
- hexdag/core/orchestration/hooks.py +437 -0
- hexdag/core/orchestration/models.py +418 -0
- hexdag/core/orchestration/orchestrator.py +910 -0
- hexdag/core/orchestration/orchestrator_factory.py +275 -0
- hexdag/core/orchestration/port_wrappers.py +327 -0
- hexdag/core/orchestration/prompt/__init__.py +32 -0
- hexdag/core/orchestration/prompt/template.py +332 -0
- hexdag/core/pipeline_builder/__init__.py +21 -0
- hexdag/core/pipeline_builder/component_instantiator.py +386 -0
- hexdag/core/pipeline_builder/include_tag.py +265 -0
- hexdag/core/pipeline_builder/pipeline_config.py +133 -0
- hexdag/core/pipeline_builder/py_tag.py +223 -0
- hexdag/core/pipeline_builder/tag_discovery.py +268 -0
- hexdag/core/pipeline_builder/yaml_builder.py +1196 -0
- hexdag/core/pipeline_builder/yaml_validator.py +569 -0
- hexdag/core/ports/__init__.py +65 -0
- hexdag/core/ports/api_call.py +133 -0
- hexdag/core/ports/database.py +489 -0
- hexdag/core/ports/embedding.py +215 -0
- hexdag/core/ports/executor.py +237 -0
- hexdag/core/ports/file_storage.py +117 -0
- hexdag/core/ports/healthcheck.py +87 -0
- hexdag/core/ports/llm.py +551 -0
- hexdag/core/ports/memory.py +70 -0
- hexdag/core/ports/observer_manager.py +130 -0
- hexdag/core/ports/secret.py +145 -0
- hexdag/core/ports/tool_router.py +94 -0
- hexdag/core/ports_builder.py +623 -0
- hexdag/core/protocols.py +273 -0
- hexdag/core/resolver.py +304 -0
- hexdag/core/schema/__init__.py +9 -0
- hexdag/core/schema/generator.py +742 -0
- hexdag/core/secrets.py +242 -0
- hexdag/core/types.py +413 -0
- hexdag/core/utils/async_warnings.py +206 -0
- hexdag/core/utils/schema_conversion.py +78 -0
- hexdag/core/utils/sql_validation.py +86 -0
- hexdag/core/validation/secure_json.py +148 -0
- hexdag/core/yaml_macro.py +517 -0
- hexdag/mcp_server.py +3120 -0
- hexdag/studio/__init__.py +10 -0
- hexdag/studio/build_ui.py +92 -0
- hexdag/studio/server/__init__.py +1 -0
- hexdag/studio/server/main.py +100 -0
- hexdag/studio/server/routes/__init__.py +9 -0
- hexdag/studio/server/routes/execute.py +208 -0
- hexdag/studio/server/routes/export.py +558 -0
- hexdag/studio/server/routes/files.py +207 -0
- hexdag/studio/server/routes/plugins.py +419 -0
- hexdag/studio/server/routes/validate.py +220 -0
- hexdag/studio/ui/index.html +13 -0
- hexdag/studio/ui/package-lock.json +2992 -0
- hexdag/studio/ui/package.json +31 -0
- hexdag/studio/ui/postcss.config.js +6 -0
- hexdag/studio/ui/public/hexdag.svg +5 -0
- hexdag/studio/ui/src/App.tsx +251 -0
- hexdag/studio/ui/src/components/Canvas.tsx +408 -0
- hexdag/studio/ui/src/components/ContextMenu.tsx +187 -0
- hexdag/studio/ui/src/components/FileBrowser.tsx +123 -0
- hexdag/studio/ui/src/components/Header.tsx +181 -0
- hexdag/studio/ui/src/components/HexdagNode.tsx +193 -0
- hexdag/studio/ui/src/components/NodeInspector.tsx +512 -0
- hexdag/studio/ui/src/components/NodePalette.tsx +262 -0
- hexdag/studio/ui/src/components/NodePortsSection.tsx +403 -0
- hexdag/studio/ui/src/components/PluginManager.tsx +347 -0
- hexdag/studio/ui/src/components/PortsEditor.tsx +481 -0
- hexdag/studio/ui/src/components/PythonEditor.tsx +195 -0
- hexdag/studio/ui/src/components/ValidationPanel.tsx +105 -0
- hexdag/studio/ui/src/components/YamlEditor.tsx +196 -0
- hexdag/studio/ui/src/components/index.ts +8 -0
- hexdag/studio/ui/src/index.css +92 -0
- hexdag/studio/ui/src/main.tsx +10 -0
- hexdag/studio/ui/src/types/index.ts +123 -0
- hexdag/studio/ui/src/vite-env.d.ts +1 -0
- hexdag/studio/ui/tailwind.config.js +29 -0
- hexdag/studio/ui/tsconfig.json +37 -0
- hexdag/studio/ui/tsconfig.node.json +13 -0
- hexdag/studio/ui/vite.config.ts +35 -0
- hexdag/visualization/__init__.py +69 -0
- hexdag/visualization/dag_visualizer.py +1020 -0
- hexdag-0.5.0.dev1.dist-info/METADATA +369 -0
- hexdag-0.5.0.dev1.dist-info/RECORD +261 -0
- hexdag-0.5.0.dev1.dist-info/WHEEL +4 -0
- hexdag-0.5.0.dev1.dist-info/entry_points.txt +4 -0
- hexdag-0.5.0.dev1.dist-info/licenses/LICENSE +190 -0
- hexdag_plugins/.gitignore +43 -0
- hexdag_plugins/README.md +73 -0
- hexdag_plugins/__init__.py +1 -0
- hexdag_plugins/azure/LICENSE +21 -0
- hexdag_plugins/azure/README.md +414 -0
- hexdag_plugins/azure/__init__.py +21 -0
- hexdag_plugins/azure/azure_blob_adapter.py +450 -0
- hexdag_plugins/azure/azure_cosmos_adapter.py +383 -0
- hexdag_plugins/azure/azure_keyvault_adapter.py +314 -0
- hexdag_plugins/azure/azure_openai_adapter.py +415 -0
- hexdag_plugins/azure/pyproject.toml +107 -0
- hexdag_plugins/azure/tests/__init__.py +1 -0
- hexdag_plugins/azure/tests/test_azure_blob_adapter.py +350 -0
- hexdag_plugins/azure/tests/test_azure_cosmos_adapter.py +323 -0
- hexdag_plugins/azure/tests/test_azure_keyvault_adapter.py +330 -0
- hexdag_plugins/azure/tests/test_azure_openai_adapter.py +329 -0
- hexdag_plugins/hexdag_etl/README.md +168 -0
- hexdag_plugins/hexdag_etl/__init__.py +53 -0
- hexdag_plugins/hexdag_etl/examples/01_simple_pandas_transform.py +270 -0
- hexdag_plugins/hexdag_etl/examples/02_simple_pandas_only.py +149 -0
- hexdag_plugins/hexdag_etl/examples/03_file_io_pipeline.py +109 -0
- hexdag_plugins/hexdag_etl/examples/test_pandas_transform.py +84 -0
- hexdag_plugins/hexdag_etl/hexdag.toml +25 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/__init__.py +48 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/__init__.py +13 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/api_extract.py +230 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/base_node_factory.py +181 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/file_io.py +415 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/outlook.py +492 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/pandas_transform.py +563 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/sql_extract_load.py +112 -0
- hexdag_plugins/hexdag_etl/pyproject.toml +82 -0
- hexdag_plugins/hexdag_etl/test_transform.py +54 -0
- hexdag_plugins/hexdag_etl/tests/test_plugin_integration.py +62 -0
- hexdag_plugins/mysql_adapter/LICENSE +21 -0
- hexdag_plugins/mysql_adapter/README.md +224 -0
- hexdag_plugins/mysql_adapter/__init__.py +6 -0
- hexdag_plugins/mysql_adapter/mysql_adapter.py +408 -0
- hexdag_plugins/mysql_adapter/pyproject.toml +93 -0
- hexdag_plugins/mysql_adapter/tests/test_mysql_adapter.py +259 -0
- hexdag_plugins/storage/README.md +184 -0
- hexdag_plugins/storage/__init__.py +19 -0
- hexdag_plugins/storage/file/__init__.py +5 -0
- hexdag_plugins/storage/file/local.py +325 -0
- hexdag_plugins/storage/ports/__init__.py +5 -0
- hexdag_plugins/storage/ports/vector_store.py +236 -0
- hexdag_plugins/storage/sql/__init__.py +7 -0
- hexdag_plugins/storage/sql/base.py +187 -0
- hexdag_plugins/storage/sql/mysql.py +27 -0
- hexdag_plugins/storage/sql/postgresql.py +27 -0
- hexdag_plugins/storage/tests/__init__.py +1 -0
- hexdag_plugins/storage/tests/test_local_file_storage.py +161 -0
- hexdag_plugins/storage/tests/test_sql_adapters.py +212 -0
- hexdag_plugins/storage/vector/__init__.py +7 -0
- hexdag_plugins/storage/vector/chromadb.py +223 -0
- hexdag_plugins/storage/vector/in_memory.py +285 -0
- 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
|
+
]
|