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,275 @@
|
|
|
1
|
+
"""Orchestrator Factory - Creates orchestrator instances from pipeline configuration.
|
|
2
|
+
|
|
3
|
+
This factory bridges the gap between declarative YAML configuration and the
|
|
4
|
+
runtime orchestrator, instantiating adapters and policies from their specs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from hexdag.core.logging import get_logger
|
|
10
|
+
from hexdag.core.orchestration.orchestrator import Orchestrator
|
|
11
|
+
from hexdag.core.pipeline_builder.component_instantiator import ComponentInstantiator
|
|
12
|
+
from hexdag.core.pipeline_builder.pipeline_config import PipelineConfig
|
|
13
|
+
from hexdag.core.ports_builder import PortsBuilder
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OrchestratorFactory:
|
|
19
|
+
"""Factory for creating orchestrator instances from pipeline configuration.
|
|
20
|
+
|
|
21
|
+
This factory handles the complex task of:
|
|
22
|
+
1. Instantiating adapter instances from port specs
|
|
23
|
+
2. Instantiating policy instances from policy specs
|
|
24
|
+
3. Wiring everything together into a configured orchestrator
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
--------
|
|
28
|
+
```python
|
|
29
|
+
from hexdag.core.pipeline_builder.yaml_builder import YamlPipelineBuilder
|
|
30
|
+
from hexdag.core.orchestration.orchestrator_factory import OrchestratorFactory
|
|
31
|
+
|
|
32
|
+
# Parse YAML pipeline
|
|
33
|
+
builder = YamlPipelineBuilder()
|
|
34
|
+
graph, pipeline_config = builder.build_from_yaml_file("pipeline.yaml")
|
|
35
|
+
|
|
36
|
+
factory = OrchestratorFactory()
|
|
37
|
+
orchestrator = factory.create_orchestrator(pipeline_config)
|
|
38
|
+
|
|
39
|
+
# Execute the pipeline
|
|
40
|
+
result = await orchestrator.aexecute_dag(graph, initial_input={"query": "..."})
|
|
41
|
+
```
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
"""Initialize the orchestrator factory."""
|
|
46
|
+
self.component_instantiator = ComponentInstantiator()
|
|
47
|
+
|
|
48
|
+
def create_orchestrator(
|
|
49
|
+
self,
|
|
50
|
+
pipeline_config: PipelineConfig,
|
|
51
|
+
max_concurrent_nodes: int = 10,
|
|
52
|
+
strict_validation: bool = False,
|
|
53
|
+
default_node_timeout: float | None = None,
|
|
54
|
+
additional_ports: dict[str, Any] | None = None,
|
|
55
|
+
) -> Orchestrator:
|
|
56
|
+
"""Create an orchestrator instance from pipeline configuration.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
pipeline_config : PipelineConfig
|
|
61
|
+
Pipeline configuration with ports, policies, and metadata
|
|
62
|
+
max_concurrent_nodes : int, optional
|
|
63
|
+
Maximum number of nodes to execute concurrently, by default 10
|
|
64
|
+
strict_validation : bool, optional
|
|
65
|
+
If True, raise errors on validation failure, by default False
|
|
66
|
+
default_node_timeout : float | None, optional
|
|
67
|
+
Default timeout in seconds for each node, by default None
|
|
68
|
+
additional_ports : dict[str, Any] | None, optional
|
|
69
|
+
Additional ports to merge with configured ports, by default None
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
Orchestrator
|
|
74
|
+
Configured orchestrator ready to execute pipelines
|
|
75
|
+
|
|
76
|
+
Examples
|
|
77
|
+
--------
|
|
78
|
+
Basic usage with global ports::
|
|
79
|
+
|
|
80
|
+
factory = OrchestratorFactory()
|
|
81
|
+
|
|
82
|
+
orchestrator = factory.create_orchestrator(
|
|
83
|
+
pipeline_config=config,
|
|
84
|
+
max_concurrent_nodes=5,
|
|
85
|
+
strict_validation=True,
|
|
86
|
+
default_node_timeout=60.0,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
Notes
|
|
90
|
+
-----
|
|
91
|
+
**Known Limitations (Phase 4)**:
|
|
92
|
+
|
|
93
|
+
- **Type-specific ports** (``type_ports`` in YAML): Parsed and validated
|
|
94
|
+
but not yet used during execution. All nodes currently receive global ports.
|
|
95
|
+
|
|
96
|
+
- **Node-level port overrides**: Not yet implemented. Requires orchestrator
|
|
97
|
+
changes to resolve ports per-node at execution time.
|
|
98
|
+
|
|
99
|
+
**Workaround for Advanced Port Configuration**:
|
|
100
|
+
|
|
101
|
+
For type-specific or node-level port customization, use PortsBuilder
|
|
102
|
+
programmatically::
|
|
103
|
+
|
|
104
|
+
from hexdag.core.ports_builder import PortsBuilder
|
|
105
|
+
|
|
106
|
+
builder = (
|
|
107
|
+
PortsBuilder()
|
|
108
|
+
.with_llm(MockLLM()) # Global default
|
|
109
|
+
.for_type("agent", llm=OpenAIAdapter(model="gpt-4"))
|
|
110
|
+
.for_node("researcher", llm=AnthropicAdapter(model="claude-3"))
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Pass as additional_ports
|
|
114
|
+
orchestrator = factory.create_orchestrator(
|
|
115
|
+
pipeline_config,
|
|
116
|
+
additional_ports=builder.build()
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
These features are planned for future implementation when orchestrator
|
|
120
|
+
supports per-node port resolution.
|
|
121
|
+
"""
|
|
122
|
+
logger.info(
|
|
123
|
+
"Creating orchestrator from pipeline config: {} ports, {} type_ports, {} policies",
|
|
124
|
+
len(pipeline_config.ports),
|
|
125
|
+
len(pipeline_config.type_ports),
|
|
126
|
+
len(pipeline_config.policies),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Step 1: Build PortsConfiguration if type_ports or node_ports are configured
|
|
130
|
+
use_ports_config = bool(pipeline_config.type_ports)
|
|
131
|
+
|
|
132
|
+
if use_ports_config:
|
|
133
|
+
ports_config = self._build_ports_configuration(pipeline_config, additional_ports)
|
|
134
|
+
# Note: PortsConfiguration converts dicts to tuples for immutability
|
|
135
|
+
global_ports = {k: v.port for k, v in (ports_config.global_ports or ())}
|
|
136
|
+
else:
|
|
137
|
+
# Simple case: just global ports
|
|
138
|
+
ports_config = None
|
|
139
|
+
global_ports = self._instantiate_ports(pipeline_config.ports)
|
|
140
|
+
if additional_ports:
|
|
141
|
+
global_ports.update(additional_ports)
|
|
142
|
+
|
|
143
|
+
# Step 2: Create orchestrator with configured ports
|
|
144
|
+
orchestrator = Orchestrator(
|
|
145
|
+
max_concurrent_nodes=max_concurrent_nodes,
|
|
146
|
+
ports=ports_config if ports_config else global_ports,
|
|
147
|
+
strict_validation=strict_validation,
|
|
148
|
+
default_node_timeout=default_node_timeout,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
logger.info(
|
|
152
|
+
"✅ Orchestrator created with {} ports",
|
|
153
|
+
len(global_ports),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return orchestrator
|
|
157
|
+
|
|
158
|
+
def _instantiate_ports(self, port_specs: dict[str, dict[str, Any]]) -> dict[str, Any]:
|
|
159
|
+
"""Instantiate adapter instances from port specifications.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
port_specs : dict[str, dict[str, Any]]
|
|
164
|
+
Map of port_name -> adapter dict spec
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
dict[str, Any]
|
|
169
|
+
Map of port_name -> adapter_instance
|
|
170
|
+
"""
|
|
171
|
+
ports = {}
|
|
172
|
+
|
|
173
|
+
for port_name, port_spec in port_specs.items():
|
|
174
|
+
try:
|
|
175
|
+
logger.debug("Instantiating port: {} = {}", port_name, port_spec)
|
|
176
|
+
adapter = self.component_instantiator.instantiate_adapter(port_spec, port_name)
|
|
177
|
+
ports[port_name] = adapter
|
|
178
|
+
logger.debug("✅ Port instantiated: {}", port_name)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error(
|
|
181
|
+
"Failed to instantiate port '{}' from spec '{}': {}",
|
|
182
|
+
port_name,
|
|
183
|
+
port_spec,
|
|
184
|
+
e,
|
|
185
|
+
)
|
|
186
|
+
raise
|
|
187
|
+
|
|
188
|
+
return ports
|
|
189
|
+
|
|
190
|
+
def _build_ports_configuration(
|
|
191
|
+
self, pipeline_config: PipelineConfig, additional_ports: dict[str, Any] | None
|
|
192
|
+
) -> Any:
|
|
193
|
+
"""Build a PortsConfiguration from pipeline config.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
pipeline_config : PipelineConfig
|
|
198
|
+
Pipeline configuration with ports, type_ports
|
|
199
|
+
additional_ports : dict[str, Any] | None
|
|
200
|
+
Additional ports to merge
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
PortsConfiguration
|
|
205
|
+
Hierarchical ports configuration with type-specific support
|
|
206
|
+
"""
|
|
207
|
+
from hexdag.core.orchestration.models import PortConfig, PortsConfiguration
|
|
208
|
+
|
|
209
|
+
# Step 1: Instantiate global ports
|
|
210
|
+
global_ports_dict = self._instantiate_ports(pipeline_config.ports)
|
|
211
|
+
if additional_ports:
|
|
212
|
+
global_ports_dict.update(additional_ports)
|
|
213
|
+
|
|
214
|
+
# Wrap in PortConfig
|
|
215
|
+
global_ports = {k: PortConfig(port=v) for k, v in global_ports_dict.items()}
|
|
216
|
+
|
|
217
|
+
# Step 2: Instantiate type-specific ports
|
|
218
|
+
type_ports = None
|
|
219
|
+
if pipeline_config.type_ports:
|
|
220
|
+
type_ports = {}
|
|
221
|
+
for node_type, type_port_specs in pipeline_config.type_ports.items():
|
|
222
|
+
type_port_instances = self._instantiate_ports(type_port_specs)
|
|
223
|
+
type_ports[node_type] = {
|
|
224
|
+
k: PortConfig(port=v) for k, v in type_port_instances.items()
|
|
225
|
+
}
|
|
226
|
+
logger.debug(
|
|
227
|
+
"Configured {} ports for node type '{}'",
|
|
228
|
+
len(type_port_instances),
|
|
229
|
+
node_type,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Step 3: TODO: Add node-level port support when available in YAML builder
|
|
233
|
+
|
|
234
|
+
return PortsConfiguration(
|
|
235
|
+
global_ports=global_ports,
|
|
236
|
+
type_ports=type_ports,
|
|
237
|
+
node_ports=None, # Not yet implemented
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def create_ports_builder(self, pipeline_config: PipelineConfig) -> PortsBuilder:
|
|
241
|
+
"""Create a PortsBuilder from pipeline configuration (convenience method).
|
|
242
|
+
|
|
243
|
+
This method provides an alternative workflow using PortsBuilder for
|
|
244
|
+
advanced use cases that need type-specific or node-specific port overrides.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
pipeline_config : PipelineConfig
|
|
249
|
+
Pipeline configuration with ports
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
PortsBuilder
|
|
254
|
+
Builder with ports configured from pipeline config
|
|
255
|
+
|
|
256
|
+
Examples
|
|
257
|
+
--------
|
|
258
|
+
```python
|
|
259
|
+
factory = OrchestratorFactory()
|
|
260
|
+
|
|
261
|
+
builder = factory.create_ports_builder(pipeline_config)
|
|
262
|
+
|
|
263
|
+
# Optionally add node-specific overrides
|
|
264
|
+
builder.for_node("researcher", llm=custom_llm)
|
|
265
|
+
|
|
266
|
+
ports = builder.build()
|
|
267
|
+
```
|
|
268
|
+
"""
|
|
269
|
+
ports = self._instantiate_ports(pipeline_config.ports)
|
|
270
|
+
|
|
271
|
+
builder = PortsBuilder()
|
|
272
|
+
for port_name, port_instance in ports.items():
|
|
273
|
+
builder.with_custom(port_name, port_instance)
|
|
274
|
+
|
|
275
|
+
return builder
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""Port wrappers that provide observability and control.
|
|
2
|
+
|
|
3
|
+
These wrappers intercept port method calls to:
|
|
4
|
+
1. **Observability** - Emit events (LLMPromptSent, LLMResponseReceived, etc.)
|
|
5
|
+
2. **Control** - Enable policy-based control (rate limiting, caching, retry, fallback)
|
|
6
|
+
3. **Metrics** - Track duration and performance
|
|
7
|
+
4. **Extensibility** - Provide hooks for custom behavior
|
|
8
|
+
|
|
9
|
+
This allows centralized control over infrastructure interactions without
|
|
10
|
+
polluting business logic in nodes and macros.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from hexdag.core.context import get_current_node_name, get_observer_manager
|
|
17
|
+
from hexdag.core.logging import get_logger
|
|
18
|
+
from hexdag.core.orchestration.events import (
|
|
19
|
+
LLMPromptSent,
|
|
20
|
+
LLMResponseReceived,
|
|
21
|
+
ToolCalled,
|
|
22
|
+
ToolCompleted,
|
|
23
|
+
)
|
|
24
|
+
from hexdag.core.ports.llm import LLM, LLMResponse, MessageList, SupportsGeneration
|
|
25
|
+
|
|
26
|
+
logger = get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ObservableLLMWrapper:
|
|
30
|
+
"""Wraps an LLM port for observability and policy-based control.
|
|
31
|
+
|
|
32
|
+
This wrapper provides:
|
|
33
|
+
- Automatic event emission for all LLM calls
|
|
34
|
+
- Policy evaluation before/after calls (future: rate limiting, caching)
|
|
35
|
+
- Duration tracking and performance metrics
|
|
36
|
+
- Transparent forwarding to underlying LLM
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, llm: LLM):
|
|
40
|
+
"""Initialize wrapper with underlying LLM port.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
llm : LLM
|
|
45
|
+
The underlying LLM port to wrap
|
|
46
|
+
"""
|
|
47
|
+
self._llm = llm
|
|
48
|
+
|
|
49
|
+
async def aresponse(self, messages: MessageList, **kwargs: Any) -> str | None:
|
|
50
|
+
"""Call LLM with observability and policy control.
|
|
51
|
+
|
|
52
|
+
This method:
|
|
53
|
+
1. Emits LLMPromptSent event (for observability)
|
|
54
|
+
2. Calls underlying LLM
|
|
55
|
+
3. Emits LLMResponseReceived event with duration
|
|
56
|
+
4. (Future: Policy evaluation for rate limiting, caching, retry)
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
messages : MessageList
|
|
61
|
+
Messages to send to LLM
|
|
62
|
+
**kwargs : Any
|
|
63
|
+
Additional LLM parameters
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
str | None
|
|
68
|
+
LLM response text
|
|
69
|
+
"""
|
|
70
|
+
node_name = get_current_node_name() or "unknown"
|
|
71
|
+
|
|
72
|
+
# Emit prompt sent event (OBSERVABILITY)
|
|
73
|
+
if observer_mgr := get_observer_manager():
|
|
74
|
+
await observer_mgr.notify(
|
|
75
|
+
LLMPromptSent(
|
|
76
|
+
node_name=node_name,
|
|
77
|
+
messages=[{"role": m.role, "content": m.content} for m in messages],
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# TODO(hexdag-team): Add policy evaluation here (CONTROL) #noqa: TD003
|
|
82
|
+
# if policy_mgr := get_policy_manager():
|
|
83
|
+
# policy_response = await policy_mgr.evaluate(LLMCallPolicy(...))
|
|
84
|
+
# if policy_response.signal == PolicySignal.SKIP:
|
|
85
|
+
# return policy_response.data # Cached response
|
|
86
|
+
# elif policy_response.signal == PolicySignal.FAIL:
|
|
87
|
+
# raise RateLimitError("LLM rate limit exceeded")
|
|
88
|
+
|
|
89
|
+
# Call underlying LLM
|
|
90
|
+
start_time = time.perf_counter()
|
|
91
|
+
|
|
92
|
+
# Check if the adapter supports generation
|
|
93
|
+
if isinstance(self._llm, SupportsGeneration):
|
|
94
|
+
response = await self._llm.aresponse(messages, **kwargs)
|
|
95
|
+
else:
|
|
96
|
+
raise NotImplementedError(
|
|
97
|
+
f"LLM adapter {type(self._llm).__name__} does not support text generation. "
|
|
98
|
+
"It must implement SupportsGeneration protocol."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
102
|
+
|
|
103
|
+
# Emit response received event (OBSERVABILITY)
|
|
104
|
+
if observer_mgr := get_observer_manager():
|
|
105
|
+
await observer_mgr.notify(
|
|
106
|
+
LLMResponseReceived(
|
|
107
|
+
node_name=node_name, response=response or "", duration_ms=duration_ms
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return response
|
|
112
|
+
|
|
113
|
+
async def aresponse_with_tools(
|
|
114
|
+
self,
|
|
115
|
+
messages: MessageList,
|
|
116
|
+
tools: list[dict[str, Any]],
|
|
117
|
+
tool_choice: str | dict[str, Any] = "auto",
|
|
118
|
+
**kwargs: Any,
|
|
119
|
+
) -> LLMResponse:
|
|
120
|
+
"""Call LLM with tools and emit events.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
messages : MessageList
|
|
125
|
+
Messages to send to LLM
|
|
126
|
+
tools : list[dict[str, Any]]
|
|
127
|
+
Tool definitions
|
|
128
|
+
tool_choice : str | dict[str, Any]
|
|
129
|
+
Tool choice strategy
|
|
130
|
+
**kwargs : Any
|
|
131
|
+
Additional LLM parameters
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
LLMResponse
|
|
136
|
+
Structured response with tool calls
|
|
137
|
+
"""
|
|
138
|
+
node_name = get_current_node_name() or "unknown"
|
|
139
|
+
|
|
140
|
+
# Emit prompt sent event
|
|
141
|
+
if observer_mgr := get_observer_manager():
|
|
142
|
+
await observer_mgr.notify(
|
|
143
|
+
LLMPromptSent(
|
|
144
|
+
node_name=node_name,
|
|
145
|
+
messages=[{"role": m.role, "content": m.content} for m in messages],
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Call underlying LLM
|
|
150
|
+
start_time = time.perf_counter()
|
|
151
|
+
response = await self._llm.aresponse_with_tools(messages, tools, tool_choice, **kwargs)
|
|
152
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
153
|
+
|
|
154
|
+
# Emit response received event
|
|
155
|
+
if observer_mgr := get_observer_manager():
|
|
156
|
+
await observer_mgr.notify(
|
|
157
|
+
LLMResponseReceived(
|
|
158
|
+
node_name=node_name, response=response.content or "", duration_ms=duration_ms
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return response
|
|
163
|
+
|
|
164
|
+
def __getattr__(self, name: str) -> Any:
|
|
165
|
+
"""Forward all other attribute access to underlying LLM."""
|
|
166
|
+
return getattr(self._llm, name)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class ObservableToolRouterWrapper:
|
|
170
|
+
"""Wraps a ToolRouter port for observability and policy-based control.
|
|
171
|
+
|
|
172
|
+
This wrapper provides:
|
|
173
|
+
- Automatic event emission for all tool calls (ToolCalled, ToolCompleted)
|
|
174
|
+
- Policy evaluation before/after calls (future: auth, rate limiting)
|
|
175
|
+
- Duration tracking and performance metrics
|
|
176
|
+
- Error handling and logging
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def __init__(self, tool_router: Any):
|
|
180
|
+
"""Initialize wrapper with underlying tool router.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
tool_router : Any
|
|
185
|
+
The underlying tool router to wrap
|
|
186
|
+
"""
|
|
187
|
+
self._tool_router = tool_router
|
|
188
|
+
|
|
189
|
+
async def acall_tool(self, tool_name: str, params: dict[str, Any]) -> Any:
|
|
190
|
+
"""Call tool with observability and policy control.
|
|
191
|
+
|
|
192
|
+
This method:
|
|
193
|
+
1. Emits ToolCalled event (for observability)
|
|
194
|
+
2. Calls underlying tool
|
|
195
|
+
3. Emits ToolCompleted event with duration and result
|
|
196
|
+
4. (Future: Policy evaluation for auth, rate limiting)
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
tool_name : str
|
|
201
|
+
Name of the tool to call
|
|
202
|
+
params : dict[str, Any]
|
|
203
|
+
Tool parameters
|
|
204
|
+
|
|
205
|
+
Returns
|
|
206
|
+
-------
|
|
207
|
+
Any
|
|
208
|
+
Tool execution result
|
|
209
|
+
"""
|
|
210
|
+
node_name = get_current_node_name() or "unknown"
|
|
211
|
+
|
|
212
|
+
# Emit tool called event (OBSERVABILITY)
|
|
213
|
+
if observer_mgr := get_observer_manager():
|
|
214
|
+
await observer_mgr.notify(
|
|
215
|
+
ToolCalled(node_name=node_name, tool_name=tool_name, params=params)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# TODO(hexdag-team): Add policy evaluation here (CONTROL) #noqa: TD003
|
|
219
|
+
# if policy_mgr := get_policy_manager():
|
|
220
|
+
# policy_response = await policy_mgr.evaluate(ToolCallPolicy(...))
|
|
221
|
+
# if policy_response.signal == PolicySignal.FAIL:
|
|
222
|
+
# raise AuthorizationError(f"Tool '{tool_name}' not authorized")
|
|
223
|
+
|
|
224
|
+
# Call underlying tool
|
|
225
|
+
start_time = time.perf_counter()
|
|
226
|
+
try:
|
|
227
|
+
result = await self._tool_router.acall_tool(tool_name, params)
|
|
228
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
229
|
+
|
|
230
|
+
# Emit tool completed event (OBSERVABILITY)
|
|
231
|
+
if observer_mgr := get_observer_manager():
|
|
232
|
+
await observer_mgr.notify(
|
|
233
|
+
ToolCompleted(
|
|
234
|
+
node_name=node_name,
|
|
235
|
+
tool_name=tool_name,
|
|
236
|
+
result=result,
|
|
237
|
+
duration_ms=duration_ms,
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
245
|
+
logger.error(f"Tool '{tool_name}' failed in {duration_ms:.2f}ms: {e}")
|
|
246
|
+
|
|
247
|
+
# Still emit completed event with error info
|
|
248
|
+
if observer_mgr := get_observer_manager():
|
|
249
|
+
await observer_mgr.notify(
|
|
250
|
+
ToolCompleted(
|
|
251
|
+
node_name=node_name,
|
|
252
|
+
tool_name=tool_name,
|
|
253
|
+
result={"error": str(e)},
|
|
254
|
+
duration_ms=duration_ms,
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
raise
|
|
259
|
+
|
|
260
|
+
def __getattr__(self, name: str) -> Any:
|
|
261
|
+
"""Forward all other attribute access to underlying tool router."""
|
|
262
|
+
return getattr(self._tool_router, name)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def wrap_llm_port(llm: Any) -> Any:
|
|
266
|
+
"""Wrap LLM port with event emission if it implements the LLM protocol.
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
llm : Any
|
|
271
|
+
The LLM port to potentially wrap
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
Any
|
|
276
|
+
Wrapped LLM if it has aresponse method, otherwise original object
|
|
277
|
+
"""
|
|
278
|
+
if hasattr(llm, "aresponse"):
|
|
279
|
+
return ObservableLLMWrapper(llm)
|
|
280
|
+
return llm
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def wrap_tool_router_port(tool_router: Any) -> Any:
|
|
284
|
+
"""Wrap ToolRouter port with event emission.
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
tool_router : Any
|
|
289
|
+
The tool router to potentially wrap
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
Any
|
|
294
|
+
Wrapped tool router if it has acall_tool method, otherwise original object
|
|
295
|
+
"""
|
|
296
|
+
if hasattr(tool_router, "acall_tool"):
|
|
297
|
+
return ObservableToolRouterWrapper(tool_router)
|
|
298
|
+
return tool_router
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def wrap_ports_with_observability(ports: dict[str, Any]) -> dict[str, Any]:
|
|
302
|
+
"""Wrap all ports with event-emitting wrappers.
|
|
303
|
+
|
|
304
|
+
This wraps:
|
|
305
|
+
- LLM ports: Emit LLMPromptSent/LLMResponseReceived events
|
|
306
|
+
- ToolRouter ports: Emit ToolCalled/ToolCompleted events
|
|
307
|
+
- (Future: Database, Memory, etc.)
|
|
308
|
+
|
|
309
|
+
Parameters
|
|
310
|
+
----------
|
|
311
|
+
ports : dict[str, Any]
|
|
312
|
+
Dictionary of port name to port adapter
|
|
313
|
+
|
|
314
|
+
Returns
|
|
315
|
+
-------
|
|
316
|
+
dict[str, Any]
|
|
317
|
+
Dictionary with wrapped ports
|
|
318
|
+
"""
|
|
319
|
+
wrapped = {}
|
|
320
|
+
for name, port in ports.items():
|
|
321
|
+
if name == "llm":
|
|
322
|
+
wrapped[name] = wrap_llm_port(port)
|
|
323
|
+
elif name == "tool_router":
|
|
324
|
+
wrapped[name] = wrap_tool_router_port(port)
|
|
325
|
+
else:
|
|
326
|
+
wrapped[name] = port
|
|
327
|
+
return wrapped
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Prompt template utilities for the application layer.
|
|
2
|
+
|
|
3
|
+
This module contains prompt template classes used by application nodes for rendering templated
|
|
4
|
+
prompts with variable substitution.
|
|
5
|
+
|
|
6
|
+
Note: ChatPromptTemplate, FewShotPromptTemplate, and ChatFewShotTemplate have been moved to
|
|
7
|
+
hexdag.builtin.prompts.base but are re-exported here for backward compatibility.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Core template (minimal)
|
|
11
|
+
# Advanced templates (from builtin, re-exported for backward compatibility)
|
|
12
|
+
from hexdag.builtin.prompts.base import (
|
|
13
|
+
ChatFewShotTemplate,
|
|
14
|
+
ChatPromptTemplate,
|
|
15
|
+
FewShotPromptTemplate,
|
|
16
|
+
)
|
|
17
|
+
from hexdag.core.orchestration.prompt.template import PromptTemplate
|
|
18
|
+
|
|
19
|
+
# Type aliases for template types (used across the framework)
|
|
20
|
+
PromptInput = (
|
|
21
|
+
str | PromptTemplate | ChatPromptTemplate | ChatFewShotTemplate | FewShotPromptTemplate
|
|
22
|
+
)
|
|
23
|
+
TemplateType = PromptTemplate | ChatPromptTemplate | ChatFewShotTemplate | FewShotPromptTemplate
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"PromptTemplate",
|
|
27
|
+
"FewShotPromptTemplate",
|
|
28
|
+
"ChatPromptTemplate",
|
|
29
|
+
"ChatFewShotTemplate",
|
|
30
|
+
"PromptInput",
|
|
31
|
+
"TemplateType",
|
|
32
|
+
]
|