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,242 @@
|
|
|
1
|
+
"""Simplified BaseNodeFactory for creating nodes with Pydantic models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Any, cast
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, create_model
|
|
9
|
+
|
|
10
|
+
from hexdag.core.domain.dag import NodeSpec
|
|
11
|
+
from hexdag.core.orchestration.prompt.template import PromptTemplate
|
|
12
|
+
from hexdag.core.protocols import is_schema_type
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseNodeFactory(ABC):
|
|
16
|
+
"""Minimal base class for node factories with Pydantic models."""
|
|
17
|
+
|
|
18
|
+
# Note: Event emission has been removed as it's now handled by the orchestrator
|
|
19
|
+
# The new event system uses ObserverManager at the orchestrator level
|
|
20
|
+
|
|
21
|
+
def create_pydantic_model(
|
|
22
|
+
self, name: str, schema: dict[str, Any] | type[BaseModel] | type[Any] | None
|
|
23
|
+
) -> type[BaseModel] | None:
|
|
24
|
+
"""Create a Pydantic model from a schema.
|
|
25
|
+
|
|
26
|
+
Raises
|
|
27
|
+
------
|
|
28
|
+
ValueError
|
|
29
|
+
If schema type is not supported
|
|
30
|
+
"""
|
|
31
|
+
if schema is None:
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
if is_schema_type(schema):
|
|
35
|
+
return schema # type: ignore[return-value] # is_schema_type checks for BaseModel subclass
|
|
36
|
+
|
|
37
|
+
if isinstance(schema, dict):
|
|
38
|
+
# String type names mapping (for when field_type is a string)
|
|
39
|
+
type_map = {
|
|
40
|
+
"str": str,
|
|
41
|
+
"int": int,
|
|
42
|
+
"float": float,
|
|
43
|
+
"bool": bool,
|
|
44
|
+
"list": list,
|
|
45
|
+
"dict": dict,
|
|
46
|
+
"Any": Any,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
field_definitions: dict[str, Any] = {}
|
|
50
|
+
for field_name, field_type in schema.items():
|
|
51
|
+
# Dispatch based on field_type's type using match pattern
|
|
52
|
+
match field_type:
|
|
53
|
+
case str():
|
|
54
|
+
# String type names - convert to actual types
|
|
55
|
+
actual_type = type_map.get(field_type, Any)
|
|
56
|
+
field_definitions[field_name] = (actual_type, ...)
|
|
57
|
+
case type():
|
|
58
|
+
# Already a type
|
|
59
|
+
field_definitions[field_name] = (field_type, ...)
|
|
60
|
+
case tuple():
|
|
61
|
+
# Already in the correct format (type, default)
|
|
62
|
+
field_definitions[field_name] = field_type
|
|
63
|
+
case _:
|
|
64
|
+
# Unknown type specification - use Any
|
|
65
|
+
field_definitions[field_name] = (Any, ...)
|
|
66
|
+
|
|
67
|
+
return create_model(name, **field_definitions)
|
|
68
|
+
|
|
69
|
+
# At this point, schema should be a type
|
|
70
|
+
try:
|
|
71
|
+
return cast("type[Any] | None", create_model(name, value=(schema, ...)))
|
|
72
|
+
except (TypeError, AttributeError) as e:
|
|
73
|
+
# If we get here, schema is an unexpected type
|
|
74
|
+
raise ValueError(
|
|
75
|
+
f"Schema must be a dict, type, or Pydantic model, got {type(schema).__name__}"
|
|
76
|
+
) from e
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def infer_input_schema_from_template(
|
|
80
|
+
template: str | PromptTemplate,
|
|
81
|
+
special_params: set[str] | None = None,
|
|
82
|
+
) -> dict[str, Any]:
|
|
83
|
+
"""Infer input schema from template variables with configurable filtering.
|
|
84
|
+
|
|
85
|
+
This method extracts variable names from a prompt template and creates
|
|
86
|
+
a schema dictionary mapping those variables to string types. It supports
|
|
87
|
+
filtering out special parameters that are not user inputs.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
template : str | PromptTemplate
|
|
92
|
+
The prompt template to analyze. Can be a string or PromptTemplate instance.
|
|
93
|
+
special_params : set[str] | None, optional
|
|
94
|
+
Set of parameter names to exclude from the schema (e.g., "context_history").
|
|
95
|
+
If None, no filtering is applied.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
dict[str, Any]
|
|
100
|
+
Schema dictionary mapping variable names to str type.
|
|
101
|
+
Returns {"input": str} if no variables found.
|
|
102
|
+
|
|
103
|
+
Examples
|
|
104
|
+
--------
|
|
105
|
+
>>> BaseNodeFactory.infer_input_schema_from_template("Hello {{name}}")
|
|
106
|
+
{'name': <class 'str'>}
|
|
107
|
+
|
|
108
|
+
>>> BaseNodeFactory.infer_input_schema_from_template(
|
|
109
|
+
... "Process {{user}} with {{context_history}}",
|
|
110
|
+
... special_params={"context_history"}
|
|
111
|
+
... )
|
|
112
|
+
{'user': <class 'str'>}
|
|
113
|
+
|
|
114
|
+
>>> BaseNodeFactory.infer_input_schema_from_template("No variables")
|
|
115
|
+
{'input': <class 'str'>}
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
if isinstance(template, str):
|
|
119
|
+
template = PromptTemplate(template)
|
|
120
|
+
|
|
121
|
+
variables = getattr(template, "input_vars", [])
|
|
122
|
+
|
|
123
|
+
if special_params:
|
|
124
|
+
variables = [v for v in variables if v not in special_params]
|
|
125
|
+
|
|
126
|
+
if not variables:
|
|
127
|
+
return {"input": str}
|
|
128
|
+
|
|
129
|
+
schema: dict[str, Any] = {}
|
|
130
|
+
for var in variables:
|
|
131
|
+
base_var = var.split(".")[0]
|
|
132
|
+
# Double-check against special params for nested variables
|
|
133
|
+
if not special_params or base_var not in special_params:
|
|
134
|
+
schema[base_var] = str
|
|
135
|
+
|
|
136
|
+
return schema
|
|
137
|
+
|
|
138
|
+
def _copy_required_ports_to_wrapper(self, wrapper_fn: Any) -> None:
|
|
139
|
+
"""Copy required_ports metadata from factory class to wrapper function.
|
|
140
|
+
|
|
141
|
+
This ensures port requirements are preserved when creating node functions.
|
|
142
|
+
"""
|
|
143
|
+
if hasattr(self.__class__, "_hexdag_required_ports"):
|
|
144
|
+
# _hexdag_required_ports is added dynamically by @node decorator
|
|
145
|
+
wrapper_fn._hexdag_required_ports = self.__class__._hexdag_required_ports # pyright: ignore[reportAttributeAccessIssue] # noqa: B010
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def extract_framework_params(kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
149
|
+
"""Extract framework-level parameters from kwargs.
|
|
150
|
+
|
|
151
|
+
This method provides a single place to extract NodeSpec framework params
|
|
152
|
+
(timeout, max_retries, retry config, when) from kwargs. All node factories
|
|
153
|
+
should use this to ensure consistent handling.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
kwargs : dict[str, Any]
|
|
158
|
+
Keyword arguments to extract from (modified in place)
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
dict[str, Any]
|
|
163
|
+
Dictionary with keys: timeout, max_retries, retry_delay, retry_backoff,
|
|
164
|
+
retry_max_delay, when. Values are None if not present in kwargs.
|
|
165
|
+
|
|
166
|
+
Examples
|
|
167
|
+
--------
|
|
168
|
+
>>> kwargs = {
|
|
169
|
+
... "when": "status == 'active'", "timeout": 30,
|
|
170
|
+
... "max_retries": 3, "other": "value"
|
|
171
|
+
... }
|
|
172
|
+
>>> framework = BaseNodeFactory.extract_framework_params(kwargs)
|
|
173
|
+
>>> framework["timeout"]
|
|
174
|
+
30
|
|
175
|
+
>>> framework["max_retries"]
|
|
176
|
+
3
|
|
177
|
+
>>> kwargs
|
|
178
|
+
{'other': 'value'}
|
|
179
|
+
|
|
180
|
+
With retry backoff configuration::
|
|
181
|
+
|
|
182
|
+
>>> kwargs = {"max_retries": 3, "retry_delay": 1.0, "retry_backoff": 2.0}
|
|
183
|
+
>>> framework = BaseNodeFactory.extract_framework_params(kwargs)
|
|
184
|
+
>>> framework["retry_delay"]
|
|
185
|
+
1.0
|
|
186
|
+
"""
|
|
187
|
+
return {
|
|
188
|
+
"timeout": kwargs.pop("timeout", None),
|
|
189
|
+
"max_retries": kwargs.pop("max_retries", None),
|
|
190
|
+
"retry_delay": kwargs.pop("retry_delay", None),
|
|
191
|
+
"retry_backoff": kwargs.pop("retry_backoff", None),
|
|
192
|
+
"retry_max_delay": kwargs.pop("retry_max_delay", None),
|
|
193
|
+
"when": kwargs.pop("when", None),
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
def create_node_with_mapping(
|
|
197
|
+
self,
|
|
198
|
+
name: str,
|
|
199
|
+
wrapped_fn: Any,
|
|
200
|
+
input_schema: dict[str, Any] | None,
|
|
201
|
+
output_schema: dict[str, Any] | type[BaseModel] | None,
|
|
202
|
+
deps: list[str] | None = None,
|
|
203
|
+
**kwargs: Any,
|
|
204
|
+
) -> NodeSpec:
|
|
205
|
+
"""Universal NodeSpec creation."""
|
|
206
|
+
# Copy required_ports metadata to wrapper
|
|
207
|
+
self._copy_required_ports_to_wrapper(wrapped_fn)
|
|
208
|
+
|
|
209
|
+
# Extract framework-level parameters from kwargs
|
|
210
|
+
framework = self.extract_framework_params(kwargs)
|
|
211
|
+
|
|
212
|
+
input_model = self.create_pydantic_model(f"{name}Input", input_schema)
|
|
213
|
+
output_model = self.create_pydantic_model(f"{name}Output", output_schema)
|
|
214
|
+
|
|
215
|
+
return NodeSpec(
|
|
216
|
+
name=name,
|
|
217
|
+
fn=wrapped_fn,
|
|
218
|
+
in_model=input_model,
|
|
219
|
+
out_model=output_model,
|
|
220
|
+
deps=frozenset(deps or []),
|
|
221
|
+
params=kwargs,
|
|
222
|
+
timeout=framework["timeout"],
|
|
223
|
+
max_retries=framework["max_retries"],
|
|
224
|
+
retry_delay=framework["retry_delay"],
|
|
225
|
+
retry_backoff=framework["retry_backoff"],
|
|
226
|
+
retry_max_delay=framework["retry_max_delay"],
|
|
227
|
+
when=framework["when"],
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
@abstractmethod
|
|
231
|
+
def __call__(self, name: str, *args: Any, **kwargs: Any) -> NodeSpec: # noqa: ARG002
|
|
232
|
+
"""Create a NodeSpec.
|
|
233
|
+
|
|
234
|
+
Must be implemented by subclasses.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
name: Name of the node
|
|
238
|
+
*args: Additional positional arguments (unused, for subclass flexibility)
|
|
239
|
+
**kwargs: Additional keyword arguments
|
|
240
|
+
"""
|
|
241
|
+
_ = args # Marked as intentionally unused for subclass API flexibility
|
|
242
|
+
raise NotImplementedError
|