vellum-ai 1.2.0__py3-none-any.whl → 1.2.2__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 +18 -1
- vellum/client/__init__.py +3 -0
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/errors/__init__.py +10 -1
- vellum/client/errors/too_many_requests_error.py +11 -0
- vellum/client/errors/unauthorized_error.py +11 -0
- vellum/client/reference.md +94 -0
- vellum/client/resources/__init__.py +2 -0
- vellum/client/resources/events/__init__.py +4 -0
- vellum/client/resources/events/client.py +165 -0
- vellum/client/resources/events/raw_client.py +207 -0
- vellum/client/types/__init__.py +6 -0
- vellum/client/types/error_detail_response.py +22 -0
- vellum/client/types/event_create_response.py +26 -0
- vellum/client/types/execution_thinking_vellum_value.py +1 -1
- vellum/client/types/thinking_vellum_value.py +1 -1
- vellum/client/types/thinking_vellum_value_request.py +1 -1
- vellum/client/types/workflow_event.py +33 -0
- vellum/errors/too_many_requests_error.py +3 -0
- vellum/errors/unauthorized_error.py +3 -0
- vellum/prompts/blocks/compilation.py +13 -11
- vellum/resources/events/__init__.py +3 -0
- vellum/resources/events/client.py +3 -0
- vellum/resources/events/raw_client.py +3 -0
- vellum/types/error_detail_response.py +3 -0
- vellum/types/event_create_response.py +3 -0
- vellum/types/workflow_event.py +3 -0
- vellum/workflows/emitters/vellum_emitter.py +16 -69
- vellum/workflows/events/tests/test_event.py +1 -0
- vellum/workflows/events/workflow.py +3 -0
- vellum/workflows/nodes/bases/base.py +0 -1
- vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +35 -0
- vellum/workflows/nodes/displayable/bases/api_node/node.py +4 -0
- vellum/workflows/nodes/displayable/bases/api_node/tests/test_node.py +26 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +6 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +22 -0
- vellum/workflows/nodes/displayable/bases/utils.py +4 -2
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +88 -2
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +1 -0
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +85 -1
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py +12 -0
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +5 -2
- vellum/workflows/ports/port.py +1 -11
- vellum/workflows/sandbox.py +6 -3
- vellum/workflows/state/context.py +14 -0
- vellum/workflows/state/encoder.py +19 -1
- vellum/workflows/types/definition.py +4 -4
- vellum/workflows/utils/hmac.py +44 -0
- vellum/workflows/utils/vellum_variables.py +5 -3
- vellum/workflows/workflows/base.py +1 -0
- {vellum_ai-1.2.0.dist-info → vellum_ai-1.2.2.dist-info}/METADATA +1 -1
- {vellum_ai-1.2.0.dist-info → vellum_ai-1.2.2.dist-info}/RECORD +94 -76
- vellum_ee/workflows/display/nodes/base_node_display.py +19 -10
- vellum_ee/workflows/display/nodes/vellum/api_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/error_node.py +6 -4
- vellum_ee/workflows/display/nodes/vellum/final_output_node.py +6 -4
- vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +34 -15
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/note_node.py +2 -4
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -4
- vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +1 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +239 -1
- vellum_ee/workflows/display/tests/test_base_workflow_display.py +53 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +12 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +16 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +5 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +12 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +4 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +5 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +1 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +5 -0
- vellum_ee/workflows/display/utils/expressions.py +4 -0
- vellum_ee/workflows/display/utils/registry.py +46 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +1 -1
- vellum_ee/workflows/tests/test_registry.py +169 -0
- vellum_ee/workflows/tests/test_server.py +72 -0
- {vellum_ai-1.2.0.dist-info → vellum_ai-1.2.2.dist-info}/LICENSE +0 -0
- {vellum_ai-1.2.0.dist-info → vellum_ai-1.2.2.dist-info}/WHEEL +0 -0
- {vellum_ai-1.2.0.dist-info → vellum_ai-1.2.2.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,7 @@ from uuid import UUID
|
|
2
2
|
from typing import ClassVar, Generic, Optional, TypeVar
|
3
3
|
|
4
4
|
from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
|
5
|
-
from vellum.workflows.types.core import JsonObject
|
5
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
6
6
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
7
7
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
8
8
|
from vellum_ee.workflows.display.nodes.utils import to_kebab_case
|
@@ -19,6 +19,10 @@ NODE_INPUT_KEY = "node_input"
|
|
19
19
|
class BaseFinalOutputNodeDisplay(BaseNodeDisplay[_FinalOutputNodeType], Generic[_FinalOutputNodeType]):
|
20
20
|
output_name: ClassVar[Optional[str]] = None
|
21
21
|
|
22
|
+
def serialize_ports(self, display_context: "WorkflowDisplayContext") -> JsonArray:
|
23
|
+
"""Final output nodes have no ports."""
|
24
|
+
return []
|
25
|
+
|
22
26
|
def serialize(self, display_context: WorkflowDisplayContext, **_kwargs) -> JsonObject:
|
23
27
|
node = self._node
|
24
28
|
node_id = self.node_id
|
@@ -45,9 +49,7 @@ class BaseFinalOutputNodeDisplay(BaseNodeDisplay[_FinalOutputNodeType], Generic[
|
|
45
49
|
"node_input_id": str(node_input.id),
|
46
50
|
},
|
47
51
|
"inputs": [node_input.dict()],
|
48
|
-
|
49
|
-
"base": self.get_base().dict(),
|
50
|
-
"definition": self.get_definition().dict(),
|
52
|
+
**self.serialize_generic_fields(display_context),
|
51
53
|
"outputs": [
|
52
54
|
{
|
53
55
|
"id": str(self._get_output_id()),
|
@@ -45,8 +45,5 @@ class BaseGuardrailNodeDisplay(BaseNodeDisplay[_GuardrailNodeType], Generic[_Gua
|
|
45
45
|
"metric_definition_id": str(raise_if_descriptor(node.metric_definition)),
|
46
46
|
"release_tag": raise_if_descriptor(node.release_tag),
|
47
47
|
},
|
48
|
-
|
49
|
-
"base": self.get_base().dict(),
|
50
|
-
"definition": self.get_definition().dict(),
|
51
|
-
"ports": self.serialize_ports(display_context),
|
48
|
+
**self.serialize_generic_fields(display_context),
|
52
49
|
}
|
@@ -1,11 +1,17 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union
|
2
|
+
from typing import TYPE_CHECKING, Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union
|
3
3
|
|
4
4
|
from vellum import FunctionDefinition, PromptBlock, RichTextChildBlock, VellumVariable
|
5
5
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
6
6
|
from vellum.workflows.nodes import InlinePromptNode
|
7
7
|
from vellum.workflows.types.core import JsonObject
|
8
|
-
from vellum.workflows.
|
8
|
+
from vellum.workflows.types.definition import DeploymentDefinition
|
9
|
+
from vellum.workflows.types.generics import is_workflow_class
|
10
|
+
from vellum.workflows.utils.functions import (
|
11
|
+
compile_function_definition,
|
12
|
+
compile_inline_workflow_function_definition,
|
13
|
+
compile_workflow_deployment_function_definition,
|
14
|
+
)
|
9
15
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
10
16
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
11
17
|
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
@@ -14,6 +20,9 @@ from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
|
14
20
|
from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
|
15
21
|
from vellum_ee.workflows.display.vellum import NodeInput
|
16
22
|
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
25
|
+
|
17
26
|
|
18
27
|
def _contains_descriptors(obj):
|
19
28
|
"""Check if an object contains any descriptors or references that need special handling."""
|
@@ -69,7 +78,10 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
|
|
69
78
|
]
|
70
79
|
|
71
80
|
functions = (
|
72
|
-
[
|
81
|
+
[
|
82
|
+
self._generate_function_tools(function, i, display_context)
|
83
|
+
for i, function in enumerate(function_definitions)
|
84
|
+
]
|
73
85
|
if isinstance(function_definitions, list)
|
74
86
|
else []
|
75
87
|
)
|
@@ -97,19 +109,12 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
|
|
97
109
|
},
|
98
110
|
"ml_model_name": ml_model,
|
99
111
|
},
|
100
|
-
|
101
|
-
"base": self.get_base().dict(),
|
102
|
-
"definition": self.get_definition().dict(),
|
103
|
-
"trigger": {
|
104
|
-
"id": str(self.get_target_handle_id()),
|
105
|
-
"merge_behavior": node.Trigger.merge_behavior.value,
|
106
|
-
},
|
112
|
+
**self.serialize_generic_fields(display_context),
|
107
113
|
"outputs": [
|
108
114
|
{"id": str(json_display.id), "name": "json", "type": "JSON", "value": None},
|
109
115
|
{"id": str(output_display.id), "name": "text", "type": "STRING", "value": None},
|
110
116
|
{"id": str(array_display.id), "name": "results", "type": "ARRAY", "value": None},
|
111
117
|
],
|
112
|
-
"ports": self.serialize_ports(display_context),
|
113
118
|
}
|
114
119
|
attributes = self._serialize_attributes(display_context)
|
115
120
|
if attributes:
|
@@ -145,10 +150,24 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
|
|
145
150
|
|
146
151
|
return node_inputs, prompt_inputs
|
147
152
|
|
148
|
-
def _generate_function_tools(
|
149
|
-
|
150
|
-
|
151
|
-
|
153
|
+
def _generate_function_tools(
|
154
|
+
self,
|
155
|
+
function: Union[FunctionDefinition, Callable, DeploymentDefinition, Type["BaseWorkflow"]],
|
156
|
+
index: int,
|
157
|
+
display_context: WorkflowDisplayContext,
|
158
|
+
) -> JsonObject:
|
159
|
+
if isinstance(function, FunctionDefinition):
|
160
|
+
normalized_functions = function
|
161
|
+
elif is_workflow_class(function):
|
162
|
+
normalized_functions = compile_inline_workflow_function_definition(function)
|
163
|
+
elif callable(function):
|
164
|
+
normalized_functions = compile_function_definition(function)
|
165
|
+
elif isinstance(function, DeploymentDefinition):
|
166
|
+
normalized_functions = compile_workflow_deployment_function_definition(
|
167
|
+
function.model_dump(), display_context.client
|
168
|
+
)
|
169
|
+
else:
|
170
|
+
raise ValueError(f"Unsupported function type: {type(function)}")
|
152
171
|
return {
|
153
172
|
"id": str(uuid4_from_hash(f"{self.node_id}-FUNCTION_DEFINITION-{index}")),
|
154
173
|
"block_type": "FUNCTION_DEFINITION",
|
@@ -55,10 +55,7 @@ class BaseInlineSubworkflowNodeDisplay(
|
|
55
55
|
"input_variables": [workflow_input.dict() for workflow_input in workflow_inputs],
|
56
56
|
"output_variables": [workflow_output.dict() for workflow_output in workflow_outputs],
|
57
57
|
},
|
58
|
-
|
59
|
-
"base": self.get_base().dict(),
|
60
|
-
"definition": self.get_definition().dict(),
|
61
|
-
"ports": self.serialize_ports(display_context),
|
58
|
+
**self.serialize_generic_fields(display_context),
|
62
59
|
}
|
63
60
|
|
64
61
|
def _generate_node_and_workflow_inputs(
|
@@ -79,8 +79,5 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
|
|
79
79
|
"item_input_id": item_workflow_input_id,
|
80
80
|
"index_input_id": index_workflow_input_id,
|
81
81
|
},
|
82
|
-
|
83
|
-
"base": self.get_base().dict(),
|
84
|
-
"definition": self.get_definition().dict(),
|
85
|
-
"ports": self.serialize_ports(display_context),
|
82
|
+
**self.serialize_generic_fields(display_context),
|
86
83
|
}
|
@@ -47,10 +47,7 @@ class BaseMergeNodeDisplay(BaseNodeDisplay[_MergeNodeType], Generic[_MergeNodeTy
|
|
47
47
|
"target_handles": [{"id": str(target_handle_id)} for target_handle_id in target_handle_ids],
|
48
48
|
"source_handle_id": str(self.get_source_handle_id(display_context.port_displays)),
|
49
49
|
},
|
50
|
-
|
51
|
-
"base": self.get_base().dict(),
|
52
|
-
"definition": self.get_definition().dict(),
|
53
|
-
"ports": self.serialize_ports(display_context),
|
50
|
+
**self.serialize_generic_fields(display_context),
|
54
51
|
}
|
55
52
|
|
56
53
|
def get_target_handle_ids(self) -> Optional[List[UUID]]:
|
@@ -13,7 +13,7 @@ class BaseNoteNodeDisplay(BaseNodeDisplay[_NoteNodeType], Generic[_NoteNodeType]
|
|
13
13
|
style: ClassVar[Union[Dict[str, Any], None]] = None
|
14
14
|
|
15
15
|
def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
|
16
|
-
del
|
16
|
+
del kwargs # Unused parameters
|
17
17
|
node_id = self.node_id
|
18
18
|
|
19
19
|
return {
|
@@ -25,7 +25,5 @@ class BaseNoteNodeDisplay(BaseNodeDisplay[_NoteNodeType], Generic[_NoteNodeType]
|
|
25
25
|
"text": self.text,
|
26
26
|
"style": self.style,
|
27
27
|
},
|
28
|
-
|
29
|
-
"base": self.get_base().dict(),
|
30
|
-
"definition": self.get_definition().dict(),
|
28
|
+
**self.serialize_generic_fields(display_context),
|
31
29
|
}
|
@@ -64,10 +64,7 @@ class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType]
|
|
64
64
|
"release_tag": raise_if_descriptor(node.release_tag),
|
65
65
|
"ml_model_fallbacks": list(ml_model_fallbacks) if ml_model_fallbacks else None,
|
66
66
|
},
|
67
|
-
|
68
|
-
"base": self.get_base().dict(),
|
69
|
-
"definition": self.get_definition().dict(),
|
70
|
-
"ports": self.serialize_ports(display_context),
|
67
|
+
**self.serialize_generic_fields(display_context),
|
71
68
|
"outputs": [
|
72
69
|
{"id": str(json_display.id), "name": "json", "type": "JSON", "value": None},
|
73
70
|
{"id": str(output_display.id), "name": "text", "type": "STRING", "value": None},
|
@@ -74,10 +74,7 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
|
|
74
74
|
"external_id_filters_node_input_id": str(node_inputs["external_id_filters"].id),
|
75
75
|
"metadata_filters_node_input_id": str(node_inputs["metadata_filters"].id),
|
76
76
|
},
|
77
|
-
|
78
|
-
"base": self.get_base().dict(),
|
79
|
-
"definition": self.get_definition().dict(),
|
80
|
-
"ports": self.serialize_ports(display_context),
|
77
|
+
**self.serialize_generic_fields(display_context),
|
81
78
|
}
|
82
79
|
|
83
80
|
def _generate_search_node_inputs(
|
@@ -61,8 +61,5 @@ class BaseSubworkflowDeploymentNodeDisplay(
|
|
61
61
|
"workflow_deployment_id": str(deployment.id),
|
62
62
|
"release_tag": raise_if_descriptor(node.release_tag),
|
63
63
|
},
|
64
|
-
|
65
|
-
"base": self.get_base().dict(),
|
66
|
-
"definition": self.get_definition().dict(),
|
67
|
-
"ports": self.serialize_ports(display_context),
|
64
|
+
**self.serialize_generic_fields(display_context),
|
68
65
|
}
|
@@ -66,8 +66,5 @@ class BaseTemplatingNodeDisplay(BaseNodeDisplay[_TemplatingNodeType], Generic[_T
|
|
66
66
|
"template_node_input_id": str(template_node_input.id),
|
67
67
|
"output_type": inferred_output_type,
|
68
68
|
},
|
69
|
-
|
70
|
-
"base": self.get_base().dict(),
|
71
|
-
"definition": self.get_definition().dict(),
|
72
|
-
"ports": self.serialize_ports(display_context),
|
69
|
+
**self.serialize_generic_fields(display_context),
|
73
70
|
}
|
@@ -251,6 +251,7 @@ def test_serialize_node__with_non_exist_code_input_path_with_dry_run():
|
|
251
251
|
],
|
252
252
|
},
|
253
253
|
"ports": [{"id": "7afa3858-f50c-4116-936a-a401e3b2c60f", "name": "default", "type": "DEFAULT"}],
|
254
|
+
"trigger": {"id": "3a39ea63-9f86-4891-a902-0216a7190720", "merge_behavior": "AWAIT_ANY"},
|
254
255
|
},
|
255
256
|
],
|
256
257
|
"edges": [
|
@@ -1,13 +1,31 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
|
1
3
|
from vellum.client.types.prompt_parameters import PromptParameters
|
4
|
+
from vellum.client.types.release_review_reviewer import ReleaseReviewReviewer
|
5
|
+
from vellum.client.types.workflow_deployment_release import (
|
6
|
+
ReleaseEnvironment,
|
7
|
+
ReleaseReleaseTag,
|
8
|
+
SlimReleaseReview,
|
9
|
+
WorkflowDeploymentRelease,
|
10
|
+
WorkflowDeploymentReleaseWorkflowDeployment,
|
11
|
+
WorkflowDeploymentReleaseWorkflowVersion,
|
12
|
+
)
|
2
13
|
from vellum.workflows import BaseWorkflow
|
3
14
|
from vellum.workflows.inputs import BaseInputs
|
15
|
+
from vellum.workflows.nodes.bases import BaseNode
|
4
16
|
from vellum.workflows.nodes.displayable.code_execution_node.node import CodeExecutionNode
|
5
17
|
from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
|
6
18
|
from vellum.workflows.nodes.displayable.tool_calling_node.node import ToolCallingNode
|
7
19
|
from vellum.workflows.nodes.displayable.tool_calling_node.state import ToolCallingState
|
8
20
|
from vellum.workflows.nodes.displayable.tool_calling_node.utils import create_router_node, create_tool_prompt_node
|
21
|
+
from vellum.workflows.outputs.base import BaseOutputs
|
9
22
|
from vellum.workflows.state.base import BaseState
|
10
|
-
from vellum.workflows.types.definition import
|
23
|
+
from vellum.workflows.types.definition import (
|
24
|
+
AuthorizationType,
|
25
|
+
DeploymentDefinition,
|
26
|
+
EnvironmentVariableReference,
|
27
|
+
MCPServer,
|
28
|
+
)
|
11
29
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
12
30
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
13
31
|
|
@@ -185,6 +203,58 @@ def test_serialize_node__tool_calling_node__mcp_server_api_key():
|
|
185
203
|
}
|
186
204
|
|
187
205
|
|
206
|
+
def test_serialize_node__tool_calling_node__mcp_server_no_authorization():
|
207
|
+
# GIVEN a tool calling node with an mcp server
|
208
|
+
class MyToolCallingNode(ToolCallingNode):
|
209
|
+
functions = [
|
210
|
+
MCPServer(
|
211
|
+
name="my-mcp-server",
|
212
|
+
url="https://my-mcp-server.com",
|
213
|
+
)
|
214
|
+
]
|
215
|
+
|
216
|
+
# AND a workflow with the tool calling node
|
217
|
+
class Workflow(BaseWorkflow):
|
218
|
+
graph = MyToolCallingNode
|
219
|
+
|
220
|
+
# WHEN the workflow is serialized
|
221
|
+
workflow_display = get_workflow_display(workflow_class=Workflow)
|
222
|
+
serialized_workflow: dict = workflow_display.serialize()
|
223
|
+
|
224
|
+
# THEN the node should properly serialize the mcp server
|
225
|
+
my_tool_calling_node = next(
|
226
|
+
node
|
227
|
+
for node in serialized_workflow["workflow_raw_data"]["nodes"]
|
228
|
+
if node["id"] == str(MyToolCallingNode.__id__)
|
229
|
+
)
|
230
|
+
|
231
|
+
functions_attribute = next(
|
232
|
+
attribute for attribute in my_tool_calling_node["attributes"] if attribute["name"] == "functions"
|
233
|
+
)
|
234
|
+
|
235
|
+
assert functions_attribute == {
|
236
|
+
"id": "c8957551-cb3d-49af-8053-acd256c1d852",
|
237
|
+
"name": "functions",
|
238
|
+
"value": {
|
239
|
+
"type": "CONSTANT_VALUE",
|
240
|
+
"value": {
|
241
|
+
"type": "JSON",
|
242
|
+
"value": [
|
243
|
+
{
|
244
|
+
"type": "MCP_SERVER",
|
245
|
+
"name": "my-mcp-server",
|
246
|
+
"url": "https://my-mcp-server.com",
|
247
|
+
"authorization_type": None,
|
248
|
+
"bearer_token_value": None,
|
249
|
+
"api_key_header_key": None,
|
250
|
+
"api_key_header_value": None,
|
251
|
+
}
|
252
|
+
],
|
253
|
+
},
|
254
|
+
},
|
255
|
+
}
|
256
|
+
|
257
|
+
|
188
258
|
def test_serialize_tool_router_node():
|
189
259
|
"""
|
190
260
|
Test that the tool router node created by create_router_node serializes successfully.
|
@@ -406,3 +476,171 @@ def test_serialize_node__tool_calling_node__subworkflow_with_parent_input_refere
|
|
406
476
|
"combinator": "OR",
|
407
477
|
},
|
408
478
|
}
|
479
|
+
|
480
|
+
|
481
|
+
def test_serialize_tool_prompt_node_with_inline_workflow():
|
482
|
+
"""
|
483
|
+
Test that the tool prompt node created by create_tool_prompt_node serializes successfully with inline workflow.
|
484
|
+
"""
|
485
|
+
|
486
|
+
# GIVEN a simple inline workflow for tool calling
|
487
|
+
class SimpleWorkflowInputs(BaseInputs):
|
488
|
+
message: str
|
489
|
+
|
490
|
+
class SimpleNode(BaseNode):
|
491
|
+
message = SimpleWorkflowInputs.message
|
492
|
+
|
493
|
+
class Outputs(BaseOutputs):
|
494
|
+
result: str
|
495
|
+
|
496
|
+
def run(self) -> Outputs:
|
497
|
+
return self.Outputs(result=f"Processed: {self.message}")
|
498
|
+
|
499
|
+
class SimpleInlineWorkflow(BaseWorkflow[SimpleWorkflowInputs, BaseState]):
|
500
|
+
"""A simple workflow for testing inline tool serialization."""
|
501
|
+
|
502
|
+
graph = SimpleNode
|
503
|
+
|
504
|
+
class Outputs(BaseOutputs):
|
505
|
+
result = SimpleNode.Outputs.result
|
506
|
+
|
507
|
+
# WHEN we create a tool prompt node using create_tool_prompt_node with inline workflow
|
508
|
+
tool_prompt_node = create_tool_prompt_node(
|
509
|
+
ml_model="gpt-4o-mini",
|
510
|
+
blocks=[],
|
511
|
+
functions=[SimpleInlineWorkflow],
|
512
|
+
prompt_inputs=None,
|
513
|
+
parameters=PromptParameters(),
|
514
|
+
)
|
515
|
+
|
516
|
+
tool_prompt_node_display_class = get_node_display_class(tool_prompt_node)
|
517
|
+
tool_prompt_node_display = tool_prompt_node_display_class()
|
518
|
+
|
519
|
+
# AND we create a workflow that uses this tool prompt node
|
520
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, ToolCallingState]):
|
521
|
+
graph = tool_prompt_node
|
522
|
+
|
523
|
+
# WHEN we serialize the entire workflow
|
524
|
+
workflow_display = get_workflow_display(workflow_class=TestWorkflow)
|
525
|
+
display_context = workflow_display.display_context
|
526
|
+
serialized_tool_prompt_node = tool_prompt_node_display.serialize(display_context)
|
527
|
+
|
528
|
+
# THEN prompt inputs should be serialized correctly
|
529
|
+
attributes = serialized_tool_prompt_node["attributes"]
|
530
|
+
assert isinstance(attributes, list)
|
531
|
+
prompt_inputs_attr = next(
|
532
|
+
(attr for attr in attributes if isinstance(attr, dict) and attr["name"] == "prompt_inputs"), None
|
533
|
+
)
|
534
|
+
assert prompt_inputs_attr == {
|
535
|
+
"id": "bc1320a2-23e4-4238-8b00-efbf88e91856",
|
536
|
+
"name": "prompt_inputs",
|
537
|
+
"value": {
|
538
|
+
"type": "DICTIONARY_REFERENCE",
|
539
|
+
"entries": [
|
540
|
+
{
|
541
|
+
"id": "76ceec7b-ec37-474f-ba38-2bfd27cecc5d",
|
542
|
+
"key": "chat_history",
|
543
|
+
"value": {
|
544
|
+
"type": "BINARY_EXPRESSION",
|
545
|
+
"lhs": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": []}},
|
546
|
+
"operator": "concat",
|
547
|
+
"rhs": {
|
548
|
+
"type": "WORKFLOW_STATE",
|
549
|
+
"state_variable_id": "7a1caaf5-99df-487a-8b2d-6512df2d871a",
|
550
|
+
},
|
551
|
+
},
|
552
|
+
}
|
553
|
+
],
|
554
|
+
},
|
555
|
+
}
|
556
|
+
|
557
|
+
|
558
|
+
def test_serialize_tool_prompt_node_with_workflow_deployment(vellum_client):
|
559
|
+
"""
|
560
|
+
Test that the tool prompt node serializes successfully with a workflow deployment.
|
561
|
+
"""
|
562
|
+
vellum_client.workflow_deployments.retrieve_workflow_deployment_release.return_value = WorkflowDeploymentRelease(
|
563
|
+
id="test-id",
|
564
|
+
created=datetime.now(),
|
565
|
+
environment=ReleaseEnvironment(
|
566
|
+
id="test-id",
|
567
|
+
name="test-name",
|
568
|
+
label="test-label",
|
569
|
+
),
|
570
|
+
created_by=None,
|
571
|
+
workflow_version=WorkflowDeploymentReleaseWorkflowVersion(
|
572
|
+
id="test-id",
|
573
|
+
input_variables=[],
|
574
|
+
output_variables=[],
|
575
|
+
),
|
576
|
+
deployment=WorkflowDeploymentReleaseWorkflowDeployment(name="test-name"),
|
577
|
+
description="test-description",
|
578
|
+
release_tags=[
|
579
|
+
ReleaseReleaseTag(
|
580
|
+
name="test-name",
|
581
|
+
source="USER",
|
582
|
+
)
|
583
|
+
],
|
584
|
+
reviews=[
|
585
|
+
SlimReleaseReview(
|
586
|
+
id="test-id",
|
587
|
+
created=datetime.now(),
|
588
|
+
reviewer=ReleaseReviewReviewer(
|
589
|
+
id="test-id",
|
590
|
+
full_name="test-name",
|
591
|
+
),
|
592
|
+
state="APPROVED",
|
593
|
+
)
|
594
|
+
],
|
595
|
+
)
|
596
|
+
|
597
|
+
# GIVEN a workflow deployment
|
598
|
+
workflow_deployment = DeploymentDefinition(
|
599
|
+
deployment="test-deployment",
|
600
|
+
release_tag="test-release-tag",
|
601
|
+
)
|
602
|
+
|
603
|
+
# WHEN we create a tool prompt node using create_tool_prompt_node with a workflow deployment
|
604
|
+
tool_prompt_node = create_tool_prompt_node(
|
605
|
+
ml_model="gpt-4o-mini",
|
606
|
+
blocks=[],
|
607
|
+
functions=[workflow_deployment],
|
608
|
+
prompt_inputs=None,
|
609
|
+
parameters=PromptParameters(),
|
610
|
+
)
|
611
|
+
|
612
|
+
tool_prompt_node_display_class = get_node_display_class(tool_prompt_node)
|
613
|
+
tool_prompt_node_display = tool_prompt_node_display_class()
|
614
|
+
|
615
|
+
# AND we create a workflow that uses this tool prompt node
|
616
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, ToolCallingState]):
|
617
|
+
graph = tool_prompt_node
|
618
|
+
|
619
|
+
# WHEN we serialize the entire workflow
|
620
|
+
workflow_display = get_workflow_display(workflow_class=TestWorkflow)
|
621
|
+
display_context = workflow_display.display_context
|
622
|
+
serialized_tool_prompt_node = tool_prompt_node_display.serialize(display_context)
|
623
|
+
|
624
|
+
# THEN functions attribute should be serialized correctly
|
625
|
+
attributes = serialized_tool_prompt_node["attributes"]
|
626
|
+
assert isinstance(attributes, list)
|
627
|
+
functions_attr = next((attr for attr in attributes if isinstance(attr, dict) and attr["name"] == "functions"), None)
|
628
|
+
assert functions_attr == {
|
629
|
+
"id": "6326ccc4-7cf6-4235-ba3c-a6e860b0c48b",
|
630
|
+
"name": "functions",
|
631
|
+
"value": {
|
632
|
+
"type": "CONSTANT_VALUE",
|
633
|
+
"value": {
|
634
|
+
"type": "JSON",
|
635
|
+
"value": [
|
636
|
+
{
|
637
|
+
"type": "WORKFLOW_DEPLOYMENT",
|
638
|
+
"name": "test-name",
|
639
|
+
"description": "test-description",
|
640
|
+
"deployment": "test-deployment",
|
641
|
+
"release_tag": "test-release-tag",
|
642
|
+
}
|
643
|
+
],
|
644
|
+
},
|
645
|
+
},
|
646
|
+
}
|
@@ -2,7 +2,8 @@ from uuid import UUID
|
|
2
2
|
from typing import Dict
|
3
3
|
|
4
4
|
from vellum.workflows.inputs import BaseInputs
|
5
|
-
from vellum.workflows.nodes import BaseNode
|
5
|
+
from vellum.workflows.nodes import BaseNode, InlineSubworkflowNode
|
6
|
+
from vellum.workflows.outputs.base import BaseOutputs
|
6
7
|
from vellum.workflows.ports.port import Port
|
7
8
|
from vellum.workflows.references.lazy import LazyReference
|
8
9
|
from vellum.workflows.state import BaseState
|
@@ -327,3 +328,54 @@ def test_serialize__port_with_lazy_reference():
|
|
327
328
|
},
|
328
329
|
}
|
329
330
|
]
|
331
|
+
|
332
|
+
|
333
|
+
def test_global_propagation_deep_nested_subworkflows():
|
334
|
+
# GIVEN the root workflow, a middle workflow, and an inner workflow
|
335
|
+
|
336
|
+
class RootInputs(BaseInputs):
|
337
|
+
root_param: str
|
338
|
+
|
339
|
+
class MiddleInputs(BaseInputs):
|
340
|
+
middle_param: str
|
341
|
+
|
342
|
+
class InnerInputs(BaseInputs):
|
343
|
+
inner_param: str
|
344
|
+
|
345
|
+
class InnerNode(BaseNode):
|
346
|
+
class Outputs(BaseOutputs):
|
347
|
+
done: bool
|
348
|
+
|
349
|
+
def run(self) -> Outputs:
|
350
|
+
return self.Outputs(done=True)
|
351
|
+
|
352
|
+
class InnerWorkflow(BaseWorkflow[InnerInputs, BaseState]):
|
353
|
+
graph = InnerNode
|
354
|
+
|
355
|
+
class MiddleInlineSubworkflowNode(InlineSubworkflowNode):
|
356
|
+
subworkflow_inputs = {"inner_param": "x"}
|
357
|
+
subworkflow = InnerWorkflow
|
358
|
+
|
359
|
+
class MiddleWorkflow(BaseWorkflow[MiddleInputs, BaseState]):
|
360
|
+
graph = MiddleInlineSubworkflowNode
|
361
|
+
|
362
|
+
class OuterInlineSubworkflowNode(InlineSubworkflowNode):
|
363
|
+
subworkflow_inputs = {"middle_param": "y"}
|
364
|
+
subworkflow = MiddleWorkflow
|
365
|
+
|
366
|
+
class RootWorkflow(BaseWorkflow[RootInputs, BaseState]):
|
367
|
+
graph = OuterInlineSubworkflowNode
|
368
|
+
|
369
|
+
# WHEN we build the displays
|
370
|
+
root_display = get_workflow_display(workflow_class=RootWorkflow)
|
371
|
+
middle_display = get_workflow_display(
|
372
|
+
workflow_class=MiddleWorkflow, parent_display_context=root_display.display_context
|
373
|
+
)
|
374
|
+
inner_display = get_workflow_display(
|
375
|
+
workflow_class=InnerWorkflow, parent_display_context=middle_display.display_context
|
376
|
+
)
|
377
|
+
|
378
|
+
# THEN the deepest display must include root + middle + inner inputs in its GLOBAL view
|
379
|
+
inner_global_names = {ref.name for ref in inner_display.display_context.global_workflow_input_displays.keys()}
|
380
|
+
|
381
|
+
assert inner_global_names == {"middle_param", "inner_param", "root_param"}
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py
CHANGED
@@ -203,6 +203,10 @@ def test_serialize_workflow(vellum_client):
|
|
203
203
|
"name": "SimpleAPINode",
|
204
204
|
"module": ["tests", "workflows", "basic_api_node", "workflow"],
|
205
205
|
},
|
206
|
+
"trigger": {
|
207
|
+
"id": "14b538a5-aedb-41f3-b579-039956b7c7ed",
|
208
|
+
"merge_behavior": "AWAIT_ANY",
|
209
|
+
},
|
206
210
|
"ports": [{"id": "7c33b4d3-9204-4bd5-9371-80ee34f83073", "name": "default", "type": "DEFAULT"}],
|
207
211
|
},
|
208
212
|
api_node,
|
@@ -110,6 +110,10 @@ def test_serialize_workflow_with_filepath():
|
|
110
110
|
"module": ["tests", "workflows", "basic_code_execution_node", "workflow"],
|
111
111
|
"name": "SimpleCodeExecutionNode",
|
112
112
|
},
|
113
|
+
"trigger": {
|
114
|
+
"id": "e02a2701-22c0-4533-8b00-175998e7350a",
|
115
|
+
"merge_behavior": "AWAIT_ANY",
|
116
|
+
},
|
113
117
|
"ports": [{"id": "832f81ec-427b-42a8-825c-e62c43c1f961", "name": "default", "type": "DEFAULT"}],
|
114
118
|
}
|
115
119
|
assert not DeepDiff(
|
@@ -342,6 +346,10 @@ def test_serialize_workflow_with_code():
|
|
342
346
|
"name": "SimpleCodeExecutionNode",
|
343
347
|
"module": ["tests", "workflows", "basic_code_execution_node", "workflow_with_code"],
|
344
348
|
},
|
349
|
+
"trigger": {
|
350
|
+
"id": "e02a2701-22c0-4533-8b00-175998e7350a",
|
351
|
+
"merge_behavior": "AWAIT_ANY",
|
352
|
+
},
|
345
353
|
"ports": [{"id": "832f81ec-427b-42a8-825c-e62c43c1f961", "name": "default", "type": "DEFAULT"}],
|
346
354
|
}
|
347
355
|
assert not DeepDiff(
|
@@ -602,6 +610,10 @@ def test_serialize_workflow__try_wrapped():
|
|
602
610
|
],
|
603
611
|
}
|
604
612
|
],
|
613
|
+
"trigger": {
|
614
|
+
"id": "e02a2701-22c0-4533-8b00-175998e7350a",
|
615
|
+
"merge_behavior": "AWAIT_ANY",
|
616
|
+
},
|
605
617
|
"ports": [{"id": "832f81ec-427b-42a8-825c-e62c43c1f961", "name": "default", "type": "DEFAULT"}],
|
606
618
|
}
|
607
619
|
|
@@ -424,6 +424,10 @@ def test_serialize_workflow():
|
|
424
424
|
"name": "CategoryConditionalNode",
|
425
425
|
"module": ["tests", "workflows", "basic_conditional_node", "workflow"],
|
426
426
|
},
|
427
|
+
"trigger": {
|
428
|
+
"id": "dd89e228-a23e-422b-80b2-34362c1c050e",
|
429
|
+
"merge_behavior": "AWAIT_ANY",
|
430
|
+
},
|
427
431
|
"ports": [
|
428
432
|
{
|
429
433
|
"id": "3a45b81f-95e4-4cbd-8997-bfdbe30251e8",
|
@@ -952,6 +956,10 @@ def test_conditional_node_serialize_all_operators_with_lhs_and_rhs(descriptor, o
|
|
952
956
|
"name": "SimpleConditionalNode",
|
953
957
|
"module": ["tests", "workflows", "basic_conditional_node", "workflow_with_only_one_conditional_node"],
|
954
958
|
},
|
959
|
+
"trigger": {
|
960
|
+
"id": "c6e99e94-bc8e-47a4-b75c-cc96c6bedbb0",
|
961
|
+
"merge_behavior": "AWAIT_ANY",
|
962
|
+
},
|
955
963
|
"ports": [
|
956
964
|
{
|
957
965
|
"id": "2ff87aa6-37cf-43dd-af9d-13b9198ab70a",
|
@@ -1063,6 +1071,10 @@ def test_conditional_node_serialize_all_operators_with_expression(descriptor, op
|
|
1063
1071
|
"name": "SimpleConditionalNode",
|
1064
1072
|
"module": ["tests", "workflows", "basic_conditional_node", "workflow_with_only_one_conditional_node"],
|
1065
1073
|
},
|
1074
|
+
"trigger": {
|
1075
|
+
"id": "c6e99e94-bc8e-47a4-b75c-cc96c6bedbb0",
|
1076
|
+
"merge_behavior": "AWAIT_ANY",
|
1077
|
+
},
|
1066
1078
|
"ports": [
|
1067
1079
|
{
|
1068
1080
|
"id": "2ff87aa6-37cf-43dd-af9d-13b9198ab70a",
|
@@ -1186,6 +1198,10 @@ def test_conditional_node_serialize_all_operators_with_value_and_start_and_end(d
|
|
1186
1198
|
"name": "SimpleConditionalNode",
|
1187
1199
|
"module": ["tests", "workflows", "basic_conditional_node", "workflow_with_only_one_conditional_node"],
|
1188
1200
|
},
|
1201
|
+
"trigger": {
|
1202
|
+
"id": "c6e99e94-bc8e-47a4-b75c-cc96c6bedbb0",
|
1203
|
+
"merge_behavior": "AWAIT_ANY",
|
1204
|
+
},
|
1189
1205
|
"ports": [
|
1190
1206
|
{
|
1191
1207
|
"id": "2ff87aa6-37cf-43dd-af9d-13b9198ab70a",
|