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,422 @@
|
|
|
1
|
+
"""Base prompt template classes for building composable prompts.
|
|
2
|
+
|
|
3
|
+
These are the foundational prompt types that all builtin prompts inherit from.
|
|
4
|
+
They provide the core functionality for chat-style prompts, few-shot learning,
|
|
5
|
+
and combinations thereof.
|
|
6
|
+
|
|
7
|
+
Moved from core.orchestration.prompt.template to keep core minimal and make
|
|
8
|
+
these templates part of the builtin library.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from hexdag.core.orchestration.prompt.template import PromptTemplate, _extract_variables_cached
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FewShotPromptTemplate(PromptTemplate):
|
|
21
|
+
r"""PromptTemplate with built-in support for few-shot examples.
|
|
22
|
+
|
|
23
|
+
Simplified implementation that builds the template once during initialization.
|
|
24
|
+
|
|
25
|
+
Examples
|
|
26
|
+
--------
|
|
27
|
+
Basic few-shot template::
|
|
28
|
+
|
|
29
|
+
template = FewShotPromptTemplate(
|
|
30
|
+
template="Classify: {{text}}",
|
|
31
|
+
examples=[
|
|
32
|
+
{"input": "Great product!", "output": "positive"},
|
|
33
|
+
{"input": "Terrible", "output": "negative"}
|
|
34
|
+
]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
result = template.format(text="Amazing!")
|
|
38
|
+
# Output includes examples + prompt
|
|
39
|
+
|
|
40
|
+
Custom example formatting::
|
|
41
|
+
|
|
42
|
+
def custom_formatter(ex):
|
|
43
|
+
return f"Q: {ex['input']}\nA: {ex['output']}"
|
|
44
|
+
|
|
45
|
+
template = FewShotPromptTemplate(
|
|
46
|
+
template="Q: {{question}}\nA:",
|
|
47
|
+
examples=[...],
|
|
48
|
+
format_example=custom_formatter
|
|
49
|
+
)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
template: str,
|
|
55
|
+
examples: list[dict[str, Any]] | None = None,
|
|
56
|
+
*,
|
|
57
|
+
format_example: Callable[[dict[str, Any]], str] | None = None,
|
|
58
|
+
example_separator: str = "\n\n",
|
|
59
|
+
**kwargs: Any,
|
|
60
|
+
) -> None:
|
|
61
|
+
r"""Initialize FewShotPromptTemplate with examples.
|
|
62
|
+
|
|
63
|
+
Args
|
|
64
|
+
----
|
|
65
|
+
template: Base template string with {{variables}}
|
|
66
|
+
examples: List of example dicts (typically with 'input' and 'output' keys)
|
|
67
|
+
format_example: Function to format each example (optional)
|
|
68
|
+
example_separator: String to separate examples (default: "\n\n")
|
|
69
|
+
**kwargs: Additional arguments passed to PromptTemplate
|
|
70
|
+
"""
|
|
71
|
+
self.examples: list[dict[str, Any]] = examples or []
|
|
72
|
+
self.example_separator = example_separator
|
|
73
|
+
self.base_template = template
|
|
74
|
+
|
|
75
|
+
if format_example is None:
|
|
76
|
+
|
|
77
|
+
def _default_formatter(ex: dict[str, Any]) -> str:
|
|
78
|
+
inp = ex.get("input", "")
|
|
79
|
+
out = ex.get("output", "")
|
|
80
|
+
return f"Input: {inp}\nOutput: {out}"
|
|
81
|
+
|
|
82
|
+
format_example = _default_formatter
|
|
83
|
+
|
|
84
|
+
self.format_example = format_example
|
|
85
|
+
|
|
86
|
+
full_template = self._build_template()
|
|
87
|
+
|
|
88
|
+
super().__init__(full_template, **kwargs)
|
|
89
|
+
|
|
90
|
+
def _build_template(self) -> str:
|
|
91
|
+
"""Build the complete template with examples prepended.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
str
|
|
96
|
+
Complete template string with examples and base template
|
|
97
|
+
"""
|
|
98
|
+
if not self.examples:
|
|
99
|
+
return self.base_template
|
|
100
|
+
|
|
101
|
+
examples_text = self.example_separator.join(
|
|
102
|
+
self.format_example(example) for example in self.examples
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return f"{examples_text}{self.example_separator}{self.base_template}"
|
|
106
|
+
|
|
107
|
+
def add_example(self, example: dict[str, Any]) -> None:
|
|
108
|
+
"""Add an example and rebuild the template.
|
|
109
|
+
|
|
110
|
+
Args
|
|
111
|
+
----
|
|
112
|
+
example: Example dict to add
|
|
113
|
+
"""
|
|
114
|
+
self.examples.append(example)
|
|
115
|
+
|
|
116
|
+
# Rebuild the complete template
|
|
117
|
+
self.template = self._build_template()
|
|
118
|
+
|
|
119
|
+
# Re-extract variables from the new template using cached function
|
|
120
|
+
self.input_vars = list(_extract_variables_cached(self.template))
|
|
121
|
+
|
|
122
|
+
def __add__(self, other: str | PromptTemplate) -> FewShotPromptTemplate:
|
|
123
|
+
"""Add text or template using + operator.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
FewShotPromptTemplate
|
|
128
|
+
New FewShotPromptTemplate instance with enhanced content
|
|
129
|
+
"""
|
|
130
|
+
if isinstance(other, PromptTemplate):
|
|
131
|
+
enhanced_base_template = self.base_template + other.template
|
|
132
|
+
else:
|
|
133
|
+
enhanced_base_template = self.base_template + other
|
|
134
|
+
|
|
135
|
+
return FewShotPromptTemplate(
|
|
136
|
+
enhanced_base_template,
|
|
137
|
+
self.examples,
|
|
138
|
+
format_example=self.format_example,
|
|
139
|
+
example_separator=self.example_separator,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def add(self, text: str | PromptTemplate) -> FewShotPromptTemplate:
|
|
143
|
+
"""Add text to template (alias for + operator).
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
FewShotPromptTemplate
|
|
148
|
+
New FewShotPromptTemplate instance with enhanced content
|
|
149
|
+
"""
|
|
150
|
+
return self + text
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ChatPromptTemplate(PromptTemplate):
|
|
154
|
+
"""Enhanced prompt template supporting multi-message conversations like LangChain.
|
|
155
|
+
|
|
156
|
+
Supports:
|
|
157
|
+
- System messages
|
|
158
|
+
- Conversation history from context
|
|
159
|
+
- Multi-message templates
|
|
160
|
+
- Role-based message construction
|
|
161
|
+
|
|
162
|
+
Examples
|
|
163
|
+
--------
|
|
164
|
+
Simple system + user::
|
|
165
|
+
|
|
166
|
+
template = ChatPromptTemplate(
|
|
167
|
+
system_message="You are an expert {{domain}} analyst.",
|
|
168
|
+
human_message="Analyze this {{data_type}}: {{data}}"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
messages = template.to_messages(
|
|
172
|
+
domain="financial",
|
|
173
|
+
data_type="report",
|
|
174
|
+
data="Q4 earnings..."
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
Multi-message template::
|
|
178
|
+
|
|
179
|
+
template = ChatPromptTemplate(messages=[
|
|
180
|
+
{"role": "system", "content": "You are an expert analyst."},
|
|
181
|
+
{"role": "user", "content": "Context: {{context}}"},
|
|
182
|
+
{"role": "assistant", "content": "I understand."},
|
|
183
|
+
{"role": "user", "content": "Analyze: {{data}}"}
|
|
184
|
+
])
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
def __init__(
|
|
188
|
+
self,
|
|
189
|
+
messages: list[dict[str, str]] | None = None,
|
|
190
|
+
system_message: str | None = None,
|
|
191
|
+
human_message: str | None = None,
|
|
192
|
+
**kwargs: Any,
|
|
193
|
+
) -> None:
|
|
194
|
+
"""Initialize ChatPromptTemplate with multiple message types.
|
|
195
|
+
|
|
196
|
+
Args
|
|
197
|
+
----
|
|
198
|
+
messages: List of message templates with role and content
|
|
199
|
+
system_message: System message template (optional)
|
|
200
|
+
human_message: Human/user message template (optional)
|
|
201
|
+
**kwargs: Additional arguments passed to PromptTemplate
|
|
202
|
+
"""
|
|
203
|
+
self.system_message = system_message
|
|
204
|
+
self.human_message = human_message
|
|
205
|
+
self.message_templates = messages or []
|
|
206
|
+
|
|
207
|
+
if not self.message_templates:
|
|
208
|
+
if system_message and human_message:
|
|
209
|
+
combined_template = f"{system_message}\n{human_message}"
|
|
210
|
+
elif human_message:
|
|
211
|
+
combined_template = human_message
|
|
212
|
+
elif system_message:
|
|
213
|
+
combined_template = system_message
|
|
214
|
+
else:
|
|
215
|
+
combined_template = ""
|
|
216
|
+
else:
|
|
217
|
+
combined_template = "\n".join(msg.get("content", "") for msg in self.message_templates)
|
|
218
|
+
|
|
219
|
+
super().__init__(combined_template, **kwargs)
|
|
220
|
+
|
|
221
|
+
def to_messages(
|
|
222
|
+
self,
|
|
223
|
+
system_prompt: str | None = None,
|
|
224
|
+
context_history: list[dict[str, str]] | None = None,
|
|
225
|
+
**kwargs: Any,
|
|
226
|
+
) -> list[dict[str, str]]:
|
|
227
|
+
"""Convert to role-based messages with conversation history support.
|
|
228
|
+
|
|
229
|
+
Args
|
|
230
|
+
----
|
|
231
|
+
system_prompt: Optional system prompt to include
|
|
232
|
+
context_history: Previous conversation messages from context
|
|
233
|
+
**kwargs: Variable values for template substitution
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
list[dict[str, str]]
|
|
238
|
+
List of message dictionaries ready for LLM
|
|
239
|
+
"""
|
|
240
|
+
messages = []
|
|
241
|
+
|
|
242
|
+
if system_prompt:
|
|
243
|
+
system_content = self._render_template(system_prompt, **kwargs)
|
|
244
|
+
messages.append({"role": "system", "content": system_content})
|
|
245
|
+
elif self.system_message:
|
|
246
|
+
system_content = self._render_template(self.system_message, **kwargs)
|
|
247
|
+
messages.append({"role": "system", "content": system_content})
|
|
248
|
+
|
|
249
|
+
if context_history:
|
|
250
|
+
messages.extend(context_history)
|
|
251
|
+
|
|
252
|
+
if self.message_templates:
|
|
253
|
+
for msg_template in self.message_templates:
|
|
254
|
+
role = msg_template.get("role", "user")
|
|
255
|
+
content_template = msg_template.get("content", "")
|
|
256
|
+
rendered_content = self._render_template(content_template, **kwargs)
|
|
257
|
+
messages.append({"role": role, "content": rendered_content})
|
|
258
|
+
elif self.human_message:
|
|
259
|
+
human_content = self._render_template(self.human_message, **kwargs)
|
|
260
|
+
messages.append({"role": "user", "content": human_content})
|
|
261
|
+
|
|
262
|
+
return messages
|
|
263
|
+
|
|
264
|
+
def _render_template(self, template: str, **kwargs: Any) -> str:
|
|
265
|
+
"""Render a single template string with variables.
|
|
266
|
+
|
|
267
|
+
Returns
|
|
268
|
+
-------
|
|
269
|
+
str
|
|
270
|
+
Rendered template string with variables substituted
|
|
271
|
+
"""
|
|
272
|
+
# Use the parent class variable substitution logic
|
|
273
|
+
pattern = r"\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\s*\}\}"
|
|
274
|
+
|
|
275
|
+
def replace_var(match: Any) -> str:
|
|
276
|
+
var_path = match.group(1).strip()
|
|
277
|
+
try:
|
|
278
|
+
value = self._get_nested_value(kwargs, var_path)
|
|
279
|
+
return str(value)
|
|
280
|
+
except (KeyError, AttributeError):
|
|
281
|
+
return f"{{{{{var_path}}}}}" # Keep unreplaced variables
|
|
282
|
+
|
|
283
|
+
return re.sub(pattern, replace_var, template)
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def from_messages(cls, messages: list[dict[str, str]]) -> ChatPromptTemplate:
|
|
287
|
+
"""Create ChatPromptTemplate from a list of message dictionaries.
|
|
288
|
+
|
|
289
|
+
Args
|
|
290
|
+
----
|
|
291
|
+
messages: List of messages with 'role' and 'content' keys
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
ChatPromptTemplate
|
|
296
|
+
ChatPromptTemplate instance
|
|
297
|
+
"""
|
|
298
|
+
return cls(messages=messages)
|
|
299
|
+
|
|
300
|
+
def __add__(self, other: str | PromptTemplate) -> ChatPromptTemplate:
|
|
301
|
+
"""Add text or template to system message using + operator.
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
ChatPromptTemplate
|
|
306
|
+
New ChatPromptTemplate instance with enhanced system message
|
|
307
|
+
"""
|
|
308
|
+
if isinstance(other, PromptTemplate):
|
|
309
|
+
enhanced_system = (self.system_message or "") + other.template
|
|
310
|
+
else:
|
|
311
|
+
enhanced_system = (self.system_message or "") + other
|
|
312
|
+
|
|
313
|
+
return ChatPromptTemplate(
|
|
314
|
+
messages=self.message_templates,
|
|
315
|
+
system_message=enhanced_system,
|
|
316
|
+
human_message=self.human_message,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def add(self, text: str | PromptTemplate) -> ChatPromptTemplate:
|
|
320
|
+
"""Add text to system message (alias for + operator).
|
|
321
|
+
|
|
322
|
+
Returns
|
|
323
|
+
-------
|
|
324
|
+
ChatPromptTemplate
|
|
325
|
+
New ChatPromptTemplate instance with enhanced system message
|
|
326
|
+
"""
|
|
327
|
+
return self + text
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class ChatFewShotTemplate(ChatPromptTemplate):
|
|
331
|
+
"""Chat template with few-shot examples support.
|
|
332
|
+
|
|
333
|
+
Combines the role-based messaging of ChatPromptTemplate with the example formatting of
|
|
334
|
+
FewShotPromptTemplate. Has the EXACT same API as ChatPromptTemplate, just enhanced with
|
|
335
|
+
examples.
|
|
336
|
+
|
|
337
|
+
Examples
|
|
338
|
+
--------
|
|
339
|
+
Chat with examples::
|
|
340
|
+
|
|
341
|
+
template = ChatFewShotTemplate(
|
|
342
|
+
system_message="You are a classifier",
|
|
343
|
+
human_message="Classify: {{text}}",
|
|
344
|
+
examples=[
|
|
345
|
+
{"input": "Great!", "output": "positive"},
|
|
346
|
+
{"input": "Bad", "output": "negative"}
|
|
347
|
+
]
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
messages = template.to_messages(text="Amazing!")
|
|
351
|
+
# System message includes examples
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
def __init__(
|
|
355
|
+
self,
|
|
356
|
+
messages: list[dict[str, str]] | None = None,
|
|
357
|
+
system_message: str | None = None,
|
|
358
|
+
human_message: str | None = None,
|
|
359
|
+
examples: list[dict[str, Any]] | None = None,
|
|
360
|
+
example_separator: str = "\n\n",
|
|
361
|
+
format_example: Callable[[dict[str, Any]], str] | None = None,
|
|
362
|
+
**kwargs: Any,
|
|
363
|
+
) -> None:
|
|
364
|
+
"""Initialize ChatFewShotTemplate with same API as ChatPromptTemplate plus examples.
|
|
365
|
+
|
|
366
|
+
Args
|
|
367
|
+
----
|
|
368
|
+
messages: List of message templates with role and content (same as ChatPromptTemplate)
|
|
369
|
+
system_message: System message template (same as ChatPromptTemplate)
|
|
370
|
+
human_message: Human/user message template (same as ChatPromptTemplate)
|
|
371
|
+
examples: List of example dicts for few-shot learning (NEW)
|
|
372
|
+
example_separator: String to separate examples (NEW)
|
|
373
|
+
format_example: Function to format each example (NEW)
|
|
374
|
+
**kwargs: Additional arguments passed to ChatPromptTemplate
|
|
375
|
+
"""
|
|
376
|
+
self.examples = examples or []
|
|
377
|
+
self.example_separator = example_separator
|
|
378
|
+
|
|
379
|
+
if format_example is None:
|
|
380
|
+
|
|
381
|
+
def _default_formatter(ex: dict[str, Any]) -> str:
|
|
382
|
+
inp = ex.get("input", "")
|
|
383
|
+
out = ex.get("output", "")
|
|
384
|
+
return f"Input: {inp}\nOutput: {out}"
|
|
385
|
+
|
|
386
|
+
format_example = _default_formatter
|
|
387
|
+
self.format_example = format_example
|
|
388
|
+
|
|
389
|
+
# If we have examples, enhance the system message
|
|
390
|
+
enhanced_system_message = system_message
|
|
391
|
+
if self.examples:
|
|
392
|
+
examples_text = self.example_separator.join(
|
|
393
|
+
self.format_example(example) for example in self.examples
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
if system_message:
|
|
397
|
+
enhanced_system_message = f"{examples_text}{self.example_separator}{system_message}"
|
|
398
|
+
else:
|
|
399
|
+
enhanced_system_message = examples_text
|
|
400
|
+
|
|
401
|
+
super().__init__(
|
|
402
|
+
messages=messages,
|
|
403
|
+
system_message=enhanced_system_message,
|
|
404
|
+
human_message=human_message,
|
|
405
|
+
**kwargs,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
self._original_system_message = system_message
|
|
409
|
+
|
|
410
|
+
def add_example(self, example: dict[str, Any]) -> None:
|
|
411
|
+
"""Add an example and rebuild system message."""
|
|
412
|
+
self.examples.append(example)
|
|
413
|
+
|
|
414
|
+
# Rebuild system message with new examples
|
|
415
|
+
examples_text = self.example_separator.join(self.format_example(ex) for ex in self.examples)
|
|
416
|
+
|
|
417
|
+
if self._original_system_message:
|
|
418
|
+
self.system_message = (
|
|
419
|
+
f"{examples_text}{self.example_separator}{self._original_system_message}"
|
|
420
|
+
)
|
|
421
|
+
else:
|
|
422
|
+
self.system_message = examples_text
|