vellum-ai 0.14.21__py3-none-any.whl → 0.14.23__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.
- vellum/__init__.py +122 -0
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/resources/workflow_deployments/client.py +250 -0
- vellum/client/types/__init__.py +122 -0
- vellum/client/types/api_request_parent_context.py +41 -0
- vellum/client/types/api_version_enum.py +5 -0
- vellum/client/types/base_output.py +21 -0
- vellum/client/types/code_resource_definition.py +31 -0
- vellum/client/types/external_input_descriptor.py +23 -0
- vellum/client/types/invoked_port.py +19 -0
- vellum/client/types/ml_model_usage_wrapper.py +21 -0
- vellum/client/types/node_event_display_context.py +30 -0
- vellum/client/types/node_execution_fulfilled_body.py +24 -0
- vellum/client/types/node_execution_fulfilled_event.py +49 -0
- vellum/client/types/node_execution_initiated_body.py +21 -0
- vellum/client/types/node_execution_initiated_event.py +49 -0
- vellum/client/types/node_execution_paused_body.py +20 -0
- vellum/client/types/node_execution_paused_event.py +49 -0
- vellum/client/types/node_execution_rejected_body.py +22 -0
- vellum/client/types/node_execution_rejected_event.py +49 -0
- vellum/client/types/node_execution_resumed_body.py +20 -0
- vellum/client/types/node_execution_resumed_event.py +49 -0
- vellum/client/types/node_execution_span.py +46 -0
- vellum/client/types/node_execution_span_attributes.py +19 -0
- vellum/client/types/node_execution_streaming_body.py +22 -0
- vellum/client/types/node_execution_streaming_event.py +49 -0
- vellum/client/types/node_parent_context.py +43 -0
- vellum/client/types/parent_context.py +21 -0
- vellum/client/types/prompt_deployment_parent_context.py +49 -0
- vellum/client/types/slim_workflow_execution_read.py +54 -0
- vellum/client/types/span_link.py +41 -0
- vellum/client/types/span_link_type_enum.py +5 -0
- vellum/client/types/vellum_code_resource_definition.py +25 -0
- vellum/client/types/vellum_node_execution_event.py +18 -0
- vellum/client/types/vellum_sdk_error.py +21 -0
- vellum/client/types/vellum_sdk_error_code_enum.py +20 -0
- vellum/client/types/vellum_span.py +7 -0
- vellum/client/types/vellum_workflow_execution_event.py +20 -0
- vellum/client/types/workflow_deployment_event_executions_response.py +55 -0
- vellum/client/types/workflow_deployment_parent_context.py +49 -0
- vellum/client/types/workflow_error.py +7 -0
- vellum/client/types/workflow_event_display_context.py +28 -0
- vellum/client/types/workflow_event_execution_read.py +60 -0
- vellum/client/types/workflow_execution_actual.py +30 -0
- vellum/client/types/workflow_execution_fulfilled_body.py +21 -0
- vellum/client/types/workflow_execution_fulfilled_event.py +49 -0
- vellum/client/types/workflow_execution_initiated_body.py +30 -0
- vellum/client/types/workflow_execution_initiated_event.py +53 -0
- vellum/client/types/workflow_execution_paused_body.py +22 -0
- vellum/client/types/workflow_execution_paused_event.py +49 -0
- vellum/client/types/workflow_execution_rejected_body.py +22 -0
- vellum/client/types/workflow_execution_rejected_event.py +49 -0
- vellum/client/types/workflow_execution_resumed_body.py +20 -0
- vellum/client/types/workflow_execution_resumed_event.py +49 -0
- vellum/client/types/workflow_execution_snapshotted_body.py +21 -0
- vellum/client/types/workflow_execution_snapshotted_event.py +51 -0
- vellum/client/types/workflow_execution_span.py +50 -0
- vellum/client/types/workflow_execution_span_attributes.py +19 -0
- vellum/client/types/workflow_execution_streaming_body.py +22 -0
- vellum/client/types/workflow_execution_streaming_event.py +49 -0
- vellum/client/types/workflow_execution_usage_calculation_fulfilled_body.py +22 -0
- vellum/client/types/workflow_execution_view_online_eval_metric_result.py +30 -0
- vellum/client/types/workflow_initialization_error.py +24 -0
- vellum/client/types/workflow_parent_context.py +43 -0
- vellum/client/types/workflow_sandbox_parent_context.py +44 -0
- vellum/types/api_request_parent_context.py +3 -0
- vellum/types/api_version_enum.py +3 -0
- vellum/types/base_output.py +3 -0
- vellum/types/code_resource_definition.py +3 -0
- vellum/types/external_input_descriptor.py +3 -0
- vellum/types/invoked_port.py +3 -0
- vellum/types/ml_model_usage_wrapper.py +3 -0
- vellum/types/node_event_display_context.py +3 -0
- vellum/types/node_execution_fulfilled_body.py +3 -0
- vellum/types/node_execution_fulfilled_event.py +3 -0
- vellum/types/node_execution_initiated_body.py +3 -0
- vellum/types/node_execution_initiated_event.py +3 -0
- vellum/types/node_execution_paused_body.py +3 -0
- vellum/types/node_execution_paused_event.py +3 -0
- vellum/types/node_execution_rejected_body.py +3 -0
- vellum/types/node_execution_rejected_event.py +3 -0
- vellum/types/node_execution_resumed_body.py +3 -0
- vellum/types/node_execution_resumed_event.py +3 -0
- vellum/types/node_execution_span.py +3 -0
- vellum/types/node_execution_span_attributes.py +3 -0
- vellum/types/node_execution_streaming_body.py +3 -0
- vellum/types/node_execution_streaming_event.py +3 -0
- vellum/types/node_parent_context.py +3 -0
- vellum/types/parent_context.py +3 -0
- vellum/types/prompt_deployment_parent_context.py +3 -0
- vellum/types/slim_workflow_execution_read.py +3 -0
- vellum/types/span_link.py +3 -0
- vellum/types/span_link_type_enum.py +3 -0
- vellum/types/vellum_code_resource_definition.py +3 -0
- vellum/types/vellum_node_execution_event.py +3 -0
- vellum/types/vellum_sdk_error.py +3 -0
- vellum/types/vellum_sdk_error_code_enum.py +3 -0
- vellum/types/vellum_span.py +3 -0
- vellum/types/vellum_workflow_execution_event.py +3 -0
- vellum/types/workflow_deployment_event_executions_response.py +3 -0
- vellum/types/workflow_deployment_parent_context.py +3 -0
- vellum/types/workflow_error.py +3 -0
- vellum/types/workflow_event_display_context.py +3 -0
- vellum/types/workflow_event_execution_read.py +3 -0
- vellum/types/workflow_execution_actual.py +3 -0
- vellum/types/workflow_execution_fulfilled_body.py +3 -0
- vellum/types/workflow_execution_fulfilled_event.py +3 -0
- vellum/types/workflow_execution_initiated_body.py +3 -0
- vellum/types/workflow_execution_initiated_event.py +3 -0
- vellum/types/workflow_execution_paused_body.py +3 -0
- vellum/types/workflow_execution_paused_event.py +3 -0
- vellum/types/workflow_execution_rejected_body.py +3 -0
- vellum/types/workflow_execution_rejected_event.py +3 -0
- vellum/types/workflow_execution_resumed_body.py +3 -0
- vellum/types/workflow_execution_resumed_event.py +3 -0
- vellum/types/workflow_execution_snapshotted_body.py +3 -0
- vellum/types/workflow_execution_snapshotted_event.py +3 -0
- vellum/types/workflow_execution_span.py +3 -0
- vellum/types/workflow_execution_span_attributes.py +3 -0
- vellum/types/workflow_execution_streaming_body.py +3 -0
- vellum/types/workflow_execution_streaming_event.py +3 -0
- vellum/types/workflow_execution_usage_calculation_fulfilled_body.py +3 -0
- vellum/types/workflow_execution_view_online_eval_metric_result.py +3 -0
- vellum/types/workflow_initialization_error.py +3 -0
- vellum/types/workflow_parent_context.py +3 -0
- vellum/types/workflow_sandbox_parent_context.py +3 -0
- vellum/utils/templating/constants.py +7 -2
- vellum/utils/templating/custom_filters.py +9 -0
- vellum/workflows/README.md +3 -2
- vellum/workflows/nodes/core/retry_node/tests/test_node.py +0 -23
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +18 -0
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +4 -2
- vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
- vellum/workflows/workflows/base.py +2 -2
- vellum/workflows/workflows/tests/test_base_workflow.py +10 -0
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.23.dist-info}/METADATA +2 -3
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.23.dist-info}/RECORD +147 -25
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +63 -42
- vellum_ee/workflows/display/nodes/vellum/final_output_node.py +8 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +8 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +12 -8
- vellum_ee/workflows/display/workflows/base_workflow_display.py +9 -27
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +54 -0
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +2 -2
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.23.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.23.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.23.dist-info}/entry_points.txt +0 -0
@@ -3,6 +3,7 @@ import types
|
|
3
3
|
from uuid import UUID
|
4
4
|
from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar, cast
|
5
5
|
|
6
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
6
7
|
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
7
8
|
from vellum.workflows.nodes.utils import get_wrapped_node
|
8
9
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
@@ -17,7 +18,62 @@ from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
|
17
18
|
_BaseAdornmentNodeType = TypeVar("_BaseAdornmentNodeType", bound=BaseAdornmentNode)
|
18
19
|
|
19
20
|
|
21
|
+
def _recursively_replace_wrapped_node(node_class: Type[BaseNode], wrapped_node_display_class: Type["BaseNodeDisplay"]):
|
22
|
+
if not issubclass(node_class, BaseAdornmentNode):
|
23
|
+
return
|
24
|
+
|
25
|
+
# We must edit the node display class to use __wrapped_node__ everywhere it
|
26
|
+
# references the adorned node class, which is three places:
|
27
|
+
wrapped_node_class = node_class.__wrapped_node__
|
28
|
+
if not wrapped_node_class:
|
29
|
+
raise ValueError(f"Node {node_class.__name__} must be used as an adornment with the `wrap` method.")
|
30
|
+
|
31
|
+
# 1. The node display class' parameterized type
|
32
|
+
original_base_node_display = get_original_base(wrapped_node_display_class)
|
33
|
+
original_base_node_display.__args__ = (wrapped_node_class,)
|
34
|
+
wrapped_node_display_class._node_display_registry[wrapped_node_class] = wrapped_node_display_class
|
35
|
+
wrapped_node_display_class.__annotate_node__()
|
36
|
+
|
37
|
+
# 2. The node display class' output displays
|
38
|
+
for old_output in node_class.Outputs:
|
39
|
+
new_output = getattr(wrapped_node_class.Outputs, old_output.name, None)
|
40
|
+
if new_output is None:
|
41
|
+
# If the adornment is adding a new output, such as TryNode adding an "error" output,
|
42
|
+
# we skip it, since it should not be included in the adorned node's output displays
|
43
|
+
wrapped_node_display_class.output_display.pop(old_output, None)
|
44
|
+
continue
|
45
|
+
|
46
|
+
if old_output not in wrapped_node_display_class.output_display:
|
47
|
+
# If the adorned node doesn't have an output display defined for this output, we define one
|
48
|
+
wrapped_node_display_class.output_display[new_output] = NodeOutputDisplay(
|
49
|
+
id=wrapped_node_class.__output_ids__[old_output.name],
|
50
|
+
name=old_output.name,
|
51
|
+
)
|
52
|
+
else:
|
53
|
+
wrapped_node_display_class.output_display[new_output] = wrapped_node_display_class.output_display.pop(
|
54
|
+
old_output
|
55
|
+
)
|
56
|
+
|
57
|
+
# 3. The node display class' port displays
|
58
|
+
old_ports = list(wrapped_node_display_class.port_displays.keys())
|
59
|
+
for old_port in old_ports:
|
60
|
+
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
61
|
+
wrapped_node_display_class.port_displays[new_port] = wrapped_node_display_class.port_displays.pop(old_port)
|
62
|
+
|
63
|
+
# Now we keep drilling down recursively
|
64
|
+
if (
|
65
|
+
issubclass(wrapped_node_display_class, BaseAdornmentNodeDisplay)
|
66
|
+
and wrapped_node_display_class.__wrapped_node_display__
|
67
|
+
):
|
68
|
+
_recursively_replace_wrapped_node(
|
69
|
+
wrapped_node_class,
|
70
|
+
wrapped_node_display_class.__wrapped_node_display__,
|
71
|
+
)
|
72
|
+
|
73
|
+
|
20
74
|
class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Generic[_BaseAdornmentNodeType]):
|
75
|
+
__wrapped_node_display__: Optional[Type[BaseNodeDisplay]] = None
|
76
|
+
|
21
77
|
def serialize(
|
22
78
|
self,
|
23
79
|
display_context: "WorkflowDisplayContext",
|
@@ -52,10 +108,6 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
52
108
|
if not issubclass(node_class, BaseAdornmentNode):
|
53
109
|
raise ValueError(f"Node {node_class.__name__} must be wrapped with a {BaseAdornmentNode.__name__}")
|
54
110
|
|
55
|
-
wrapped_node_class = node_class.__wrapped_node__
|
56
|
-
if not wrapped_node_class:
|
57
|
-
raise ValueError(f"Node {node_class.__name__} must be used as an adornment with the `wrap` method.")
|
58
|
-
|
59
111
|
# `mypy` is wrong here, `cls` is indexable bc it's Generic
|
60
112
|
BaseAdornmentDisplay = cls[node_class] # type: ignore[index]
|
61
113
|
|
@@ -64,6 +116,7 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
64
116
|
ns[key] = kwarg
|
65
117
|
|
66
118
|
ns["node_id"] = node_id or uuid4_from_hash(node_class.__qualname__)
|
119
|
+
ns["__wrapped_node_display__"] = wrapped_node_display_class
|
67
120
|
|
68
121
|
AdornmentDisplay = types.new_class(
|
69
122
|
re.sub(r"^Base", "", cls.__name__), bases=(BaseAdornmentDisplay,), exec_body=exec_body
|
@@ -71,43 +124,11 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
71
124
|
|
72
125
|
setattr(wrapped_node_display_class, "__adorned_by__", AdornmentDisplay)
|
73
126
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
wrapped_node_display_class._node_display_registry[wrapped_node_class] = wrapped_node_display_class
|
81
|
-
wrapped_node_display_class.__annotate_node__()
|
82
|
-
|
83
|
-
# 2. The node display class' output displays
|
84
|
-
for old_output in node_class.Outputs:
|
85
|
-
new_output = getattr(wrapped_node_class.Outputs, old_output.name, None)
|
86
|
-
if new_output is None:
|
87
|
-
# If the adornment is adding a new output, such as TryNode adding an "error" output,
|
88
|
-
# we skip it, since it should not be included in the adorned node's output displays
|
89
|
-
wrapped_node_display_class.output_display.pop(old_output, None)
|
90
|
-
continue
|
91
|
-
|
92
|
-
if old_output not in wrapped_node_display_class.output_display:
|
93
|
-
# If the adorned node doesn't have an output display defined for this output, we define one
|
94
|
-
wrapped_node_display_class.output_display[new_output] = NodeOutputDisplay(
|
95
|
-
id=wrapped_node_class.__output_ids__[old_output.name],
|
96
|
-
name=old_output.name,
|
97
|
-
)
|
98
|
-
else:
|
99
|
-
wrapped_node_display_class.output_display[new_output] = (
|
100
|
-
wrapped_node_display_class.output_display.pop(old_output)
|
101
|
-
)
|
102
|
-
|
103
|
-
# 3. The node display class' port displays
|
104
|
-
old_ports = list(wrapped_node_display_class.port_displays.keys())
|
105
|
-
for old_port in old_ports:
|
106
|
-
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
107
|
-
wrapped_node_display_class.port_displays[new_port] = wrapped_node_display_class.port_displays.pop(
|
108
|
-
old_port
|
109
|
-
)
|
110
|
-
|
111
|
-
return wrapped_node_display_class
|
127
|
+
_recursively_replace_wrapped_node(
|
128
|
+
node_class,
|
129
|
+
wrapped_node_display_class,
|
130
|
+
)
|
131
|
+
|
132
|
+
return AdornmentDisplay
|
112
133
|
|
113
134
|
return decorator
|
@@ -47,6 +47,14 @@ class BaseFinalOutputNodeDisplay(BaseNodeVellumDisplay[_FinalOutputNodeType], Ge
|
|
47
47
|
"display_data": self.get_display_data().dict(),
|
48
48
|
"base": self.get_base().dict(),
|
49
49
|
"definition": self.get_definition().dict(),
|
50
|
+
"outputs": [
|
51
|
+
{
|
52
|
+
"id": str(self._get_output_id()),
|
53
|
+
"name": node.Outputs.value.name,
|
54
|
+
"type": inferred_type,
|
55
|
+
"value": self.serialize_value(display_context, node.Outputs.value.instance),
|
56
|
+
}
|
57
|
+
],
|
50
58
|
}
|
51
59
|
|
52
60
|
def _get_output_id(self) -> UUID:
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py
CHANGED
@@ -99,4 +99,12 @@ def test_serialize_workflow():
|
|
99
99
|
"name": "BasicFinalOutputNode",
|
100
100
|
"module": ["tests", "workflows", "basic_final_output_node", "workflow"],
|
101
101
|
},
|
102
|
+
"outputs": [
|
103
|
+
{
|
104
|
+
"id": "97349956-d228-4b51-a64b-1331f788373f",
|
105
|
+
"name": "value",
|
106
|
+
"type": "STRING",
|
107
|
+
"value": {"type": "WORKFLOW_INPUT", "input_variable_id": "e39a7b63-de15-490a-ae9b-8112c767aea0"},
|
108
|
+
}
|
109
|
+
],
|
102
110
|
}
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py
CHANGED
@@ -123,6 +123,17 @@ def test_serialize_workflow__missing_final_output_node():
|
|
123
123
|
"name": "FirstFinalOutputNode",
|
124
124
|
"module": ["tests", "workflows", "complex_final_output_node", "missing_final_output_node"],
|
125
125
|
},
|
126
|
+
"outputs": [
|
127
|
+
{
|
128
|
+
"id": "5517e50d-7f40-4f7c-acb2-e329d79a25bf",
|
129
|
+
"name": "value",
|
130
|
+
"type": "STRING",
|
131
|
+
"value": {
|
132
|
+
"type": "WORKFLOW_INPUT",
|
133
|
+
"input_variable_id": "da086239-d743-4246-b666-5c91e22fb88c",
|
134
|
+
},
|
135
|
+
}
|
136
|
+
],
|
126
137
|
},
|
127
138
|
{
|
128
139
|
"id": "bb88768d-472e-4997-b7ea-de09163d1b4c",
|
@@ -156,14 +167,7 @@ def test_serialize_workflow__missing_final_output_node():
|
|
156
167
|
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
157
168
|
"base": {
|
158
169
|
"name": "FinalOutputNode",
|
159
|
-
"module": [
|
160
|
-
"vellum",
|
161
|
-
"workflows",
|
162
|
-
"nodes",
|
163
|
-
"displayable",
|
164
|
-
"final_output_node",
|
165
|
-
"node",
|
166
|
-
],
|
170
|
+
"module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
|
167
171
|
},
|
168
172
|
"definition": None,
|
169
173
|
},
|
@@ -10,7 +10,6 @@ from vellum.workflows import BaseWorkflow
|
|
10
10
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
11
11
|
from vellum.workflows.edges import Edge
|
12
12
|
from vellum.workflows.events.workflow import NodeEventDisplayContext, WorkflowEventDisplayContext
|
13
|
-
from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
|
14
13
|
from vellum.workflows.nodes.bases import BaseNode
|
15
14
|
from vellum.workflows.nodes.utils import get_unadorned_node, get_wrapped_node
|
16
15
|
from vellum.workflows.ports import Port
|
@@ -150,8 +149,6 @@ class BaseWorkflowDisplay(
|
|
150
149
|
if node_output in node_output_displays:
|
151
150
|
continue
|
152
151
|
|
153
|
-
# TODO: Make sure this output ID matches the workflow output ID of the subworkflow node's workflow
|
154
|
-
# https://app.shortcut.com/vellum/story/5660/fix-output-id-in-subworkflow-nodes
|
155
152
|
node_output_displays[node_output] = node_display.get_node_output_display(node_output)
|
156
153
|
|
157
154
|
def _enrich_node_port_displays(
|
@@ -272,8 +269,8 @@ class BaseWorkflowDisplay(
|
|
272
269
|
continue
|
273
270
|
|
274
271
|
edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
|
275
|
-
edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
|
276
|
-
edge, node_displays
|
272
|
+
edge_displays[(edge.from_port, edge.to_node)] = edge_display_overrides or self._generate_edge_display(
|
273
|
+
edge, node_displays
|
277
274
|
)
|
278
275
|
|
279
276
|
for edge in self._workflow.get_unused_edges():
|
@@ -281,8 +278,8 @@ class BaseWorkflowDisplay(
|
|
281
278
|
continue
|
282
279
|
|
283
280
|
edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
|
284
|
-
edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
|
285
|
-
edge, node_displays
|
281
|
+
edge_displays[(edge.from_port, edge.to_node)] = edge_display_overrides or self._generate_edge_display(
|
282
|
+
edge, node_displays
|
286
283
|
)
|
287
284
|
|
288
285
|
workflow_output_displays: Dict[BaseDescriptor, WorkflowOutputDisplay] = {}
|
@@ -293,11 +290,6 @@ class BaseWorkflowDisplay(
|
|
293
290
|
if not isinstance(workflow_output, OutputReference):
|
294
291
|
raise ValueError(f"{workflow_output} must be an {OutputReference.__name__}")
|
295
292
|
|
296
|
-
if not workflow_output.instance or not isinstance(
|
297
|
-
workflow_output.instance, (OutputReference, CoalesceExpression)
|
298
|
-
):
|
299
|
-
raise ValueError("Expected to find a descriptor instance on the workflow output")
|
300
|
-
|
301
293
|
workflow_output_display = self.output_displays.get(workflow_output)
|
302
294
|
workflow_output_displays[workflow_output] = (
|
303
295
|
workflow_output_display or self._generate_workflow_output_display(workflow_output)
|
@@ -443,30 +435,20 @@ class BaseWorkflowDisplay(
|
|
443
435
|
|
444
436
|
return additional_node_displays
|
445
437
|
|
446
|
-
def _generate_edge_display(
|
447
|
-
self,
|
448
|
-
edge: Edge,
|
449
|
-
node_displays: Dict[Type[BaseNode], BaseNodeDisplay],
|
450
|
-
overrides: Optional[EdgeDisplay] = None,
|
451
|
-
) -> EdgeDisplay:
|
438
|
+
def _generate_edge_display(self, edge: Edge, node_displays: Dict[Type[BaseNode], BaseNodeDisplay]) -> EdgeDisplay:
|
452
439
|
source_node = get_unadorned_node(edge.from_port.node_class)
|
453
440
|
target_node = get_unadorned_node(edge.to_node)
|
454
441
|
|
455
442
|
source_node_id = node_displays[source_node].node_id
|
456
443
|
target_node_id = node_displays[target_node].node_id
|
457
444
|
|
458
|
-
return self._generate_edge_display_from_source(source_node_id, target_node_id
|
445
|
+
return self._generate_edge_display_from_source(source_node_id, target_node_id)
|
459
446
|
|
460
447
|
def _generate_edge_display_from_source(
|
461
448
|
self,
|
462
449
|
source_node_id: UUID,
|
463
450
|
target_node_id: UUID,
|
464
|
-
overrides: Optional[EdgeDisplay] = None,
|
465
451
|
) -> EdgeDisplay:
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
else:
|
470
|
-
edge_id = uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}")
|
471
|
-
|
472
|
-
return EdgeDisplay(id=edge_id)
|
452
|
+
return EdgeDisplay(
|
453
|
+
id=uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}"),
|
454
|
+
)
|
@@ -235,3 +235,57 @@ def test_get_event_display_context__templating_node_input_display():
|
|
235
235
|
node_event_display = display_context.node_displays[MyNode.__id__]
|
236
236
|
|
237
237
|
assert node_event_display.input_display.keys() == {"inputs.foo"}
|
238
|
+
|
239
|
+
|
240
|
+
def test_get_event_display_context__node_display_for_mutiple_adornments():
|
241
|
+
# GIVEN a simple workflow with multiple adornments
|
242
|
+
@TryNode.wrap()
|
243
|
+
@RetryNode.wrap()
|
244
|
+
class MyNode(BaseNode):
|
245
|
+
class Outputs(BaseNode.Outputs):
|
246
|
+
foo: str
|
247
|
+
|
248
|
+
class MyWorkflow(BaseWorkflow):
|
249
|
+
graph = MyNode
|
250
|
+
|
251
|
+
# AND a display class for the node
|
252
|
+
node_id = uuid4()
|
253
|
+
inner_node_id = uuid4()
|
254
|
+
innermost_node_id = uuid4()
|
255
|
+
|
256
|
+
@BaseTryNodeDisplay.wrap(node_id=node_id)
|
257
|
+
@BaseRetryNodeDisplay.wrap(node_id=inner_node_id)
|
258
|
+
class MyNodeDisplay(BaseNodeDisplay[MyNode]):
|
259
|
+
node_id = innermost_node_id
|
260
|
+
|
261
|
+
# WHEN we gather the event display context
|
262
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
263
|
+
|
264
|
+
# THEN the subworkflow display should be included
|
265
|
+
assert node_id in display_context.node_displays
|
266
|
+
node_event_display = display_context.node_displays[node_id]
|
267
|
+
assert node_event_display.subworkflow_display
|
268
|
+
|
269
|
+
# AND the inner node should be included
|
270
|
+
assert inner_node_id in node_event_display.subworkflow_display.node_displays
|
271
|
+
inner_node_event_display = node_event_display.subworkflow_display.node_displays[inner_node_id]
|
272
|
+
assert inner_node_event_display.subworkflow_display
|
273
|
+
|
274
|
+
# AND the innermost node should be included
|
275
|
+
assert innermost_node_id in inner_node_event_display.subworkflow_display.node_displays
|
276
|
+
innermost_node_event_display = inner_node_event_display.subworkflow_display.node_displays[innermost_node_id]
|
277
|
+
assert not innermost_node_event_display.subworkflow_display
|
278
|
+
|
279
|
+
|
280
|
+
def test_get_event_display_context__workflow_output_display_with_none():
|
281
|
+
# GIVEN a workflow with a workflow output that is None
|
282
|
+
class MyWorkflow(BaseWorkflow):
|
283
|
+
class Outputs(BaseWorkflow.Outputs):
|
284
|
+
foo = None
|
285
|
+
bar = "baz"
|
286
|
+
|
287
|
+
# WHEN we gather the event display context
|
288
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
289
|
+
|
290
|
+
# THEN the workflow output display should be included
|
291
|
+
assert display_context.workflow_outputs.keys() == {"foo", "bar"}
|
@@ -363,8 +363,8 @@ class VellumWorkflowDisplay(
|
|
363
363
|
target_node_display = node_displays[entrypoint_target]
|
364
364
|
target_node_id = target_node_display.node_id
|
365
365
|
|
366
|
-
edge_display = self._generate_edge_display_from_source(
|
367
|
-
entrypoint_node_id, target_node_id
|
366
|
+
edge_display = edge_display_overrides or self._generate_edge_display_from_source(
|
367
|
+
entrypoint_node_id, target_node_id
|
368
368
|
)
|
369
369
|
|
370
370
|
return EntrypointVellumDisplay(id=entrypoint_id, edge_display=edge_display)
|
File without changes
|
File without changes
|
File without changes
|