vellum-ai 0.14.73__py3-none-any.whl → 0.14.74__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.
Files changed (27) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/client/core/serialization.py +0 -1
  3. vellum/client/types/secret_type_enum.py +3 -1
  4. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +2 -2
  5. vellum/workflows/nodes/displayable/bases/utils.py +2 -0
  6. vellum/workflows/nodes/experimental/tool_calling_node/node.py +5 -1
  7. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +13 -0
  8. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +14 -4
  9. vellum/workflows/state/encoder.py +2 -0
  10. vellum/workflows/types/definition.py +1 -1
  11. vellum/workflows/utils/functions.py +3 -3
  12. vellum/workflows/utils/tests/test_functions.py +7 -7
  13. {vellum_ai-0.14.73.dist-info → vellum_ai-0.14.74.dist-info}/METADATA +1 -1
  14. {vellum_ai-0.14.73.dist-info → vellum_ai-0.14.74.dist-info}/RECORD +27 -26
  15. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +36 -7
  16. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +102 -0
  17. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +90 -0
  18. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +117 -0
  19. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +7 -0
  20. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +7 -0
  21. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +62 -0
  22. vellum_ee/workflows/display/utils/expressions.py +33 -2
  23. vellum_ee/workflows/display/workflows/base_workflow_display.py +20 -6
  24. vellum_ee/workflows/tests/test_display_meta.py +41 -0
  25. {vellum_ai-0.14.73.dist-info → vellum_ai-0.14.74.dist-info}/LICENSE +0 -0
  26. {vellum_ai-0.14.73.dist-info → vellum_ai-0.14.74.dist-info}/WHEEL +0 -0
  27. {vellum_ai-0.14.73.dist-info → vellum_ai-0.14.74.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.14.73",
21
+ "X-Fern-SDK-Version": "0.14.74",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -184,7 +184,6 @@ def _convert_mapping(
184
184
  converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = (
185
185
  convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction)
186
186
  )
187
-
188
187
  return converted_object
189
188
 
190
189
 
@@ -2,4 +2,6 @@
2
2
 
3
3
  import typing
4
4
 
5
- SecretTypeEnum = typing.Union[typing.Literal["USER_DEFINED", "HMAC", "INTERNAL_API_KEY"], typing.Any]
5
+ SecretTypeEnum = typing.Union[
6
+ typing.Literal["USER_DEFINED", "HMAC", "INTERNAL_API_KEY", "EXTERNALLY_PROVISIONED"], typing.Any
7
+ ]
@@ -33,9 +33,9 @@ from vellum.workflows.types import MergeBehavior
33
33
  from vellum.workflows.types.definition import DeploymentDefinition
34
34
  from vellum.workflows.types.generics import StateType, is_workflow_class
35
35
  from vellum.workflows.utils.functions import (
36
- compile_deployment_workflow_function_definition,
37
36
  compile_function_definition,
38
37
  compile_inline_workflow_function_definition,
38
+ compile_workflow_deployment_function_definition,
39
39
  )
40
40
  from vellum.workflows.utils.pydantic_schema import normalize_json
41
41
 
@@ -115,7 +115,7 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
115
115
  normalized_functions.append(function)
116
116
  elif isinstance(function, DeploymentDefinition):
117
117
  normalized_functions.append(
118
- compile_deployment_workflow_function_definition(
118
+ compile_workflow_deployment_function_definition(
119
119
  function.model_dump(),
120
120
  vellum_client=self._context.vellum_client,
121
121
  )
@@ -52,6 +52,8 @@ def primitive_to_vellum_value(value: Any) -> VellumValue:
52
52
  return StringVellumValue(value=value)
53
53
  elif isinstance(value, enum.Enum):
54
54
  return StringVellumValue(value=value.value)
55
+ elif isinstance(value, bool):
56
+ return JsonVellumValue(value=value)
55
57
  elif isinstance(value, (int, float)):
56
58
  return NumberVellumValue(value=value)
57
59
  elif isinstance(value, list) and (
@@ -32,6 +32,7 @@ class ToolCallingNode(BaseNode):
32
32
  functions: List[Tool] - The functions that can be called
33
33
  prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values
34
34
  function_configs: Optional[Dict[str, Dict[str, Any]]] - Mapping of function names to their configuration
35
+ max_prompt_iterations: Optional[int] - Maximum number of prompt iterations before stopping
35
36
  """
36
37
 
37
38
  ml_model: ClassVar[str] = "gpt-4o-mini"
@@ -39,6 +40,7 @@ class ToolCallingNode(BaseNode):
39
40
  functions: ClassVar[List[Tool]] = []
40
41
  prompt_inputs: ClassVar[Optional[EntityInputsInterface]] = None
41
42
  function_configs: ClassVar[Optional[Dict[str, Dict[str, Any]]]] = None
43
+ max_prompt_iterations: ClassVar[Optional[int]] = 5
42
44
 
43
45
  class Outputs(BaseOutputs):
44
46
  """
@@ -65,6 +67,7 @@ class ToolCallingNode(BaseNode):
65
67
 
66
68
  class ToolCallingState(BaseState):
67
69
  chat_history: List[ChatMessage] = []
70
+ prompt_iterations: int = 0
68
71
 
69
72
  class ToolCallingWorkflow(BaseWorkflow[BaseInputs, ToolCallingState]):
70
73
  graph = self._graph
@@ -93,7 +96,7 @@ class ToolCallingNode(BaseNode):
93
96
 
94
97
  return node_outputs
95
98
  elif terminal_event.name == "workflow.execution.rejected":
96
- raise Exception(f"Workflow execution rejected: {terminal_event.error}")
99
+ raise NodeException(message=terminal_event.error.message, code=terminal_event.error.code)
97
100
 
98
101
  raise Exception(f"Unexpected workflow event: {terminal_event.name}")
99
102
 
@@ -103,6 +106,7 @@ class ToolCallingNode(BaseNode):
103
106
  blocks=self.blocks,
104
107
  functions=self.functions,
105
108
  prompt_inputs=self.prompt_inputs,
109
+ max_prompt_iterations=self.max_prompt_iterations,
106
110
  )
107
111
 
108
112
  self._function_nodes = {}
@@ -12,6 +12,7 @@ from vellum.workflows.nodes.experimental.tool_calling_node.utils import create_f
12
12
  from vellum.workflows.outputs.base import BaseOutputs
13
13
  from vellum.workflows.state.base import BaseState, StateMeta
14
14
  from vellum.workflows.state.context import WorkflowContext
15
+ from vellum.workflows.types.definition import DeploymentDefinition
15
16
 
16
17
 
17
18
  def first_function() -> str:
@@ -127,3 +128,15 @@ def test_tool_calling_node_inline_workflow_context():
127
128
  assert isinstance(function_response.content, StringChatMessageContent)
128
129
  data = json.loads(function_response.content.value)
129
130
  assert data["generated_files"] == {"script.py": "print('hello world')"}
131
+
132
+
133
+ def test_deployment_definition_release_tag_defaults_to_latest():
134
+ """
135
+ Test that when creating a DeploymentDefinition without specifying release_tag,
136
+ it defaults to "LATEST".
137
+ """
138
+ # WHEN we create a deployment definition without specifying release_tag
139
+ deployment_config = DeploymentDefinition(deployment="test-deployment")
140
+
141
+ # THEN the release_tag should default to "LATEST"
142
+ assert deployment_config.release_tag == "LATEST"
@@ -37,10 +37,16 @@ class FunctionNode(BaseNode):
37
37
 
38
38
 
39
39
  class ToolRouterNode(InlinePromptNode):
40
+ max_prompt_iterations: Optional[int] = 5
41
+
40
42
  class Trigger(InlinePromptNode.Trigger):
41
43
  merge_behavior = MergeBehavior.AWAIT_ATTRIBUTES
42
44
 
43
45
  def run(self) -> Iterator[BaseOutput]:
46
+ if self.state.prompt_iterations >= self.max_prompt_iterations:
47
+ max_iterations_message = f"Maximum number of prompt iterations `{self.max_prompt_iterations}` reached."
48
+ raise NodeException(message=max_iterations_message, code=WorkflowErrorCode.NODE_EXECUTION)
49
+
44
50
  self.prompt_inputs = {**self.prompt_inputs, "chat_history": self.state.chat_history} # type: ignore
45
51
  generator = super().run()
46
52
  for output in generator:
@@ -50,6 +56,8 @@ class ToolRouterNode(InlinePromptNode):
50
56
  if values[0].type == "STRING":
51
57
  self.state.chat_history.append(ChatMessage(role="ASSISTANT", text=values[0].value))
52
58
  elif values[0].type == "FUNCTION_CALL":
59
+ self.state.prompt_iterations += 1
60
+
53
61
  function_call = values[0].value
54
62
  if function_call is not None:
55
63
  self.state.chat_history.append(
@@ -72,6 +80,7 @@ def create_tool_router_node(
72
80
  blocks: List[PromptBlock],
73
81
  functions: List[Tool],
74
82
  prompt_inputs: Optional[EntityInputsInterface],
83
+ max_prompt_iterations: Optional[int] = None,
75
84
  ) -> Type[ToolRouterNode]:
76
85
  if functions and len(functions) > 0:
77
86
  # If we have functions, create dynamic ports for each function
@@ -120,6 +129,7 @@ def create_tool_router_node(
120
129
  "blocks": blocks,
121
130
  "functions": functions,
122
131
  "prompt_inputs": prompt_inputs,
132
+ "max_prompt_iterations": max_prompt_iterations,
123
133
  "Ports": Ports,
124
134
  "__module__": __name__,
125
135
  },
@@ -149,7 +159,7 @@ def create_function_node(
149
159
  deployment = function.deployment
150
160
  release_tag = function.release_tag
151
161
 
152
- def execute_deployment_workflow_function(self) -> BaseNode.Outputs:
162
+ def execute_workflow_deployment_function(self) -> BaseNode.Outputs:
153
163
  function_call_output = self.state.meta.node_outputs.get(tool_router_node.Outputs.results)
154
164
  if function_call_output and len(function_call_output) > 0:
155
165
  function_call = function_call_output[0]
@@ -187,10 +197,10 @@ def create_function_node(
187
197
  return self.Outputs()
188
198
 
189
199
  node = type(
190
- f"DeploymentWorkflowNode_{deployment}",
200
+ f"WorkflowDeploymentNode_{deployment}",
191
201
  (FunctionNode,),
192
202
  {
193
- "run": execute_deployment_workflow_function,
203
+ "run": execute_workflow_deployment_function,
194
204
  "__module__": __name__,
195
205
  },
196
206
  )
@@ -219,7 +229,7 @@ def create_function_node(
219
229
  elif terminal_event.name == "workflow.execution.fulfilled":
220
230
  result = terminal_event.outputs
221
231
  elif terminal_event.name == "workflow.execution.rejected":
222
- raise Exception(f"Workflow execution rejected: {terminal_event.error}")
232
+ raise NodeException(message=terminal_event.error.message, code=terminal_event.error.code)
223
233
 
224
234
  self.state.chat_history.append(
225
235
  ChatMessage(
@@ -68,6 +68,8 @@ class DefaultStateEncoder(JSONEncoder):
68
68
 
69
69
  return {
70
70
  "type": "CODE_EXECUTION",
71
+ "name": function_definition.name,
72
+ "description": function_definition.description,
71
73
  "definition": function_definition,
72
74
  "src": source_code,
73
75
  }
@@ -74,4 +74,4 @@ VellumCodeResourceDefinition = Annotated[
74
74
 
75
75
  class DeploymentDefinition(UniversalBaseModel):
76
76
  deployment: str
77
- release_tag: Optional[str] = "LATEST"
77
+ release_tag: str = "LATEST"
@@ -88,7 +88,7 @@ def _compile_default_value(default: Any) -> Any:
88
88
  return default
89
89
 
90
90
 
91
- def _compile_deployment_workflow_input(input_var: Any) -> dict[str, Any]:
91
+ def _compile_workflow_deployment_input(input_var: Any) -> dict[str, Any]:
92
92
  """
93
93
  Converts a deployment workflow input variable to a JSON schema type definition.
94
94
  """
@@ -168,7 +168,7 @@ def compile_inline_workflow_function_definition(workflow_class: Type["BaseWorkfl
168
168
  )
169
169
 
170
170
 
171
- def compile_deployment_workflow_function_definition(
171
+ def compile_workflow_deployment_function_definition(
172
172
  deployment_config: Dict[str, str],
173
173
  vellum_client: Vellum,
174
174
  ) -> FunctionDefinition:
@@ -193,7 +193,7 @@ def compile_deployment_workflow_function_definition(
193
193
  required = []
194
194
 
195
195
  for input_var in input_variables:
196
- properties[input_var.key] = _compile_deployment_workflow_input(input_var)
196
+ properties[input_var.key] = _compile_workflow_deployment_input(input_var)
197
197
 
198
198
  if input_var.required and input_var.default is None:
199
199
  required.append(input_var.key)
@@ -12,9 +12,9 @@ from vellum.workflows.inputs.base import BaseInputs
12
12
  from vellum.workflows.nodes.bases.base import BaseNode
13
13
  from vellum.workflows.state.base import BaseState
14
14
  from vellum.workflows.utils.functions import (
15
- compile_deployment_workflow_function_definition,
16
15
  compile_function_definition,
17
16
  compile_inline_workflow_function_definition,
17
+ compile_workflow_deployment_function_definition,
18
18
  )
19
19
 
20
20
 
@@ -440,7 +440,7 @@ def test_compile_inline_workflow_function_definition__optionals():
440
440
  )
441
441
 
442
442
 
443
- def test_compile_deployment_workflow_function_definition__just_name():
443
+ def test_compile_workflow_deployment_function_definition__just_name():
444
444
  # GIVEN a mock Vellum client and deployment
445
445
  mock_client = Mock()
446
446
  mock_release = Mock()
@@ -451,7 +451,7 @@ def test_compile_deployment_workflow_function_definition__just_name():
451
451
  deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
452
452
 
453
453
  # WHEN compiling the deployment workflow function
454
- compiled_function = compile_deployment_workflow_function_definition(deployment_config, mock_client)
454
+ compiled_function = compile_workflow_deployment_function_definition(deployment_config, mock_client)
455
455
 
456
456
  # THEN it should return the compiled function definition (same structure as function test)
457
457
  assert compiled_function == FunctionDefinition(
@@ -461,7 +461,7 @@ def test_compile_deployment_workflow_function_definition__just_name():
461
461
  )
462
462
 
463
463
 
464
- def test_compile_deployment_workflow_function_definition__all_args():
464
+ def test_compile_workflow_deployment_function_definition__all_args():
465
465
  # GIVEN a mock Vellum client and deployment
466
466
  mock_client = Mock()
467
467
  mock_release = Mock()
@@ -497,7 +497,7 @@ def test_compile_deployment_workflow_function_definition__all_args():
497
497
  deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
498
498
 
499
499
  # WHEN compiling the deployment workflow function
500
- compiled_function = compile_deployment_workflow_function_definition(deployment_config, mock_client)
500
+ compiled_function = compile_workflow_deployment_function_definition(deployment_config, mock_client)
501
501
 
502
502
  # THEN it should return the compiled function definition
503
503
  assert compiled_function == FunctionDefinition(
@@ -524,7 +524,7 @@ def test_compile_deployment_workflow_function_definition__all_args():
524
524
  )
525
525
 
526
526
 
527
- def test_compile_deployment_workflow_function_definition__defaults():
527
+ def test_compile_workflow_deployment_function_definition__defaults():
528
528
  # GIVEN a mock Vellum client and deployment
529
529
  mock_client = Mock()
530
530
  mock_release = Mock()
@@ -565,7 +565,7 @@ def test_compile_deployment_workflow_function_definition__defaults():
565
565
  deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
566
566
 
567
567
  # WHEN compiling the deployment workflow function
568
- compiled_function = compile_deployment_workflow_function_definition(deployment_config, mock_client)
568
+ compiled_function = compile_workflow_deployment_function_definition(deployment_config, mock_client)
569
569
 
570
570
  # THEN it should return the compiled function definition with proper default handling
571
571
  assert compiled_function == FunctionDefinition(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.73
3
+ Version: 0.14.74
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -41,7 +41,7 @@ vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=9GtbvSJUNF62
41
41
  vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=YhMsi2TG1zSR8E7IpxzzSncOyVLcvqTuGa3mr4RqHd8,2364
42
42
  vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=zo-nalsuayMqeb2GwR2OB9SFK3y2U5aG-rtwrsjdasQ,3089
43
43
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=IniO5KvO0Rw9zghFg3RFvbXBTv6Zi1iuQhaA1DLazqU,2331
44
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=1ydd1-SXCuJdPDquMrvV5JXIIhf7eBdvY2dXqDz_r5o,11035
44
+ vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=O4crEYQYJ6XGiN7pEQKxJCGGUQCQKf3rSjKYE4D-iCY,12079
45
45
  vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=f7MeoxgKrdyb1dSJsvdDtZPlp1J2Pa4njPvN3qHVktA,6028
46
46
  vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=uaZ2wcZR1J9C9iI0QWAsgNK9IlcuCz1808oxXmiYsLY,3908
47
47
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=RTP_raQWL8ZKoRKLpxLfpyXzw61TZeTCkTuM1uRLIkI,3274
@@ -57,7 +57,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=540FoWM
57
57
  vellum_ee/workflows/display/nodes/vellum/tests/test_inline_subworkflow_node.py,sha256=SKOYan-dxY4gsO0R4JyQUyWrABHBN8XImKw9Eeo4wGo,3535
58
58
  vellum_ee/workflows/display/nodes/vellum/tests/test_note_node.py,sha256=uiMB0cOxKZzos7YKnj4ef4DFa2bOvZJWIv-hfbUV6Go,1218
59
59
  vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_deployment_node.py,sha256=G-qJyTNJkpqJiEZ3kCJl86CXJINLeFyf2lM0bQHCCOs,3822
60
- vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=TtzUj3Zk3ZhwtXE_WyctCC-CmcLB1RxntyF7u-a3i6I,10077
60
+ vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=h3rXIfB349w11cMgNpqEWCI3ucTsTb30cWskXN8FoV0,14053
61
61
  vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=h93ysolmbo2viisyhRnXKHPxiDK0I_dSAbYoHFYIoO4,1953
62
62
  vellum_ee/workflows/display/nodes/vellum/tests/test_search_node.py,sha256=KvByxgbUkVyfPIVxTUBUk6a92JiJMi8eReZWxzfPExU,3864
63
63
  vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py,sha256=BUzHJgjdWnPeZxjFjHfDBKnbFjYjnbXPjc-1hne1B2Y,3965
@@ -73,7 +73,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DE
73
73
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=XOQDDRiG46etxTC7-_RUEutoNumXc02fo7oho4GYM0c,1900
75
75
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=IuoR3QrsnshZSb1ggLBvQJnTw244lHBo70KmKLd0fwk,12852
76
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=-I0LvhaLZUrdjSzb232Jd1Qp79mVGw8-8vjLOf33tIU,22066
76
+ vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=DFNp_iUxlyE-zCJBH9ab3e1jQEK-NSE9M2CQd4NCjY8,24853
77
77
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=s6_mnk0pkztU59wYpSfOFpMhAJaRjmyfxM6WJGtnD4Y,6456
78
78
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=PkSgghJDz0fpDB72HHPjLjo8LkZk-HpUkCQzRLX-iVw,40611
79
79
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=dsJr8I9AdPwMOGszirfNDzZP2Ychd94aAKuPXAzknMk,4632
@@ -84,7 +84,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_stat
84
84
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=w1BYEZXIMjWmcKGQXzhelHnKFlEAXmsgjkI9fcgqKXA,5850
85
85
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=-T0cd2jx1bC0ZNtAESF78fnYD_0nOqo8zMMLwRHUTRM,6227
86
86
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=PZ9Yec8D6e9wM2kh5eWtNvK5BghTf1RdvoSW5_pw3FM,7384
87
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=V1HtvMavVd8KkSow5D8gVPGoDNw9XGQ_7_rCZ90EaxM,18436
87
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=tljn-ueIMEW3j2q6Ni9O852sdQMjut05ox4paBeJjm8,23714
88
88
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=3Djct7lIr2TPW-wDd_t6eoNdsE0hBOj9OwKWRroXMUo,21659
89
89
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=91u9FWLErrNGfUkZ22ifk6IazGtaYoDFbZYjhxEESgI,16579
90
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=rtkAQw5awOFX3pWh0qz3f766-tTnPhToGsXVSHO4M4U,8352
@@ -93,8 +93,9 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_
93
93
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=XWrhHg_acLsRHwjstBAii9Pmes9oXFtAUWSAVF1oSBc,11225
94
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=V8b6gKghLlO7PJI8xeNdnfn8aII0W_IFQvSQBQM62UQ,7721
95
95
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=hDWtKXmGI1CKhTwTNqpu_d5RkE5n7SolMLtgd87KqTI,3856
96
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=cBkVVCfXKhPYV9azlGYdNX1yD3V39D6hcgqiFLT1JjI,25274
97
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=FgtOfaI_9rnypCG4aSt5UGDrpBdKlCp6-QV21MZNsHw,8731
96
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=6q-lTGMp5HbQba3NsHUFSit8_zEBxIVPGE8yCw4SVx0,25720
97
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=36-fpwj53JcRmo7RuHrsS6ZjJ3q4j8BC1wuKruBjIt8,9137
98
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=zlb0Kfg1zqJsz1tD_1O-hkSKhV4-gfYI6AkK4-Yjk7U,2315
98
99
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
99
100
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=J4ouI8KxbMfxQP2Zq_9cWMGYgbjCWmKzjCJEtnSJb0I,5829
100
101
  vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
@@ -102,14 +103,14 @@ vellum_ee/workflows/display/types.py,sha256=i4T7ElU5b5h-nA1i3scmEhO1BqmNDc4eJDHa
102
103
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
104
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
104
105
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
105
- vellum_ee/workflows/display/utils/expressions.py,sha256=bSZ-sRByLCsut8XcRkCFbbqYvw9p7tlIeF-HFnEm664,14354
106
+ vellum_ee/workflows/display/utils/expressions.py,sha256=KxEl8yNx0yPvy0y84qABef5DwxRYGm0jgDd89i6PhSE,15534
106
107
  vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3Vd15ijtSN0V97bpW8,1513
107
108
  vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
109
  vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
109
110
  vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
110
111
  vellum_ee/workflows/display/vellum.py,sha256=o7mq_vk2Yapu9DDKRz5l76h8EmCAypWGQYe6pryrbB8,3576
111
112
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
112
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=4Acuh71NPso48RaihHApX27aQJWP37nhq4DVzYaG3_E,40285
113
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=ZFZ8E2mIA-2aWnP9eRT_Z1s4efh7mTcdS8TQMlsICMY,40890
113
114
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
114
115
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=L7SKWJ26Ex-XXTNfHYXux7KP6I-dxE1EMQylap4Mhjs,31762
115
116
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=aaKdmWrgEe5YyV4zuDY_4E3y-l59rIHQnNGiPj2OWxQ,359
@@ -134,7 +135,7 @@ vellum_ee/workflows/tests/local_workflow/nodes/__init__.py,sha256=1F6jxUpSKfPXPj
134
135
  vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zirg0w9VKUW3QVDSdBLDqcqAMZjCL_oWbpU,297
135
136
  vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
136
137
  vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
137
- vellum_ee/workflows/tests/test_display_meta.py,sha256=DIzjNbwK1-4mlttPML6NskQ4rPVMXhj5zeOmBEyPqKI,3728
138
+ vellum_ee/workflows/tests/test_display_meta.py,sha256=PkXJVnMZs9GNooDkd59n4YTBAX3XGPQWeSSVbhehVFM,5112
138
139
  vellum_ee/workflows/tests/test_serialize_module.py,sha256=qcHbl6YaKtJebQJvw0OXCVlJXLkj6sjkbSMu05zXI0U,1874
139
140
  vellum_ee/workflows/tests/test_server.py,sha256=SsOkS6sGO7uGC4mxvk4iv8AtcXs058P9hgFHzTWmpII,14519
140
141
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
@@ -143,7 +144,7 @@ vellum/client/README.md,sha256=CuGUYnaE0Imt0KqQ4sIPaUghCjLHkF3DdEvZWu14-8s,4807
143
144
  vellum/client/__init__.py,sha256=AYopGv2ZRVn3zsU8_km6KOvEHDbXiTPCVuYVI7bWvdA,120166
144
145
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
145
146
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
146
- vellum/client/core/client_wrapper.py,sha256=NZGCmazGkAcH9HD3LVtp7biFxQCStGNJDV4zWkgIQLw,1869
147
+ vellum/client/core/client_wrapper.py,sha256=KPv6Bi7gbvtP_1ZhF5Do4HCJqNkUHW0nNYBEtS1ZDeY,1869
147
148
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
148
149
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
149
150
  vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
@@ -152,7 +153,7 @@ vellum/client/core/pydantic_utilities.py,sha256=lnpQ0SFnoVDtFp7vSHt5lmBXWeqgg03V
152
153
  vellum/client/core/query_encoder.py,sha256=ekulqNd0j8TgD7ox-Qbz7liqX8-KP9blvT9DsRCenYM,2144
153
154
  vellum/client/core/remove_none_from_dict.py,sha256=EU9SGgYidWq7SexuJbNs4-PZ-5Bl3Vppd864mS6vQZw,342
154
155
  vellum/client/core/request_options.py,sha256=h0QUNCFVdCW_7GclVySCAY2w4NhtXVBUCmHgmzaxpcg,1681
155
- vellum/client/core/serialization.py,sha256=7mRS2A0qm29_wI4e7fdpXuz0tC-UUqMbn4T4_MBDrA0,9602
156
+ vellum/client/core/serialization.py,sha256=D9h_t-RQON3-CHWs1C4ESY9B-Yd5d-l5lnTLb_X896g,9601
156
157
  vellum/client/environment.py,sha256=bcAFjoE9XXd7tiysYS90Os669IJmUMZS2JZ_ZQn0Dpg,498
157
158
  vellum/client/errors/__init__.py,sha256=HZB8vVqzDNx0M2uFJ05S5RcGTH95iVDl4v3rQ4xRqSw,343
158
159
  vellum/client/errors/bad_request_error.py,sha256=_EbO8mWqN9kFZPvIap8qa1lL_EWkRcsZe1HKV9GDWJY,264
@@ -574,7 +575,7 @@ vellum/client/types/search_results_input.py,sha256=kUfXw_NttVClGdEu9qGiZWXggFBKa
574
575
  vellum/client/types/search_results_vellum_value.py,sha256=i_7d5cANsjuFSRwI3qUgzNdz6ZyrhakuxP4lMQeUFu4,774
575
576
  vellum/client/types/search_results_vellum_value_request.py,sha256=Y36g5iLmweV4k_UiTDI9gSXf9Pn_aCwho0Z7dUNiVZU,803
576
577
  vellum/client/types/search_weights_request.py,sha256=7YdTanQ82sDztyOwZGh6Iu9HJaTiAX3QnfxDQZJUzyE,828
577
- vellum/client/types/secret_type_enum.py,sha256=wziBD_xCh-ywlrQhrGjppxiro_awjWKdIHmbHNK6-ZE,182
578
+ vellum/client/types/secret_type_enum.py,sha256=riVAwmIZxW39waRAr8at9RATpqPkBdEMTQqifHDyMOs,214
578
579
  vellum/client/types/sentence_chunker_config.py,sha256=is3t8niS19gjRtqewSkLYpskJCbRloCZ78Kfe7zs3vE,709
579
580
  vellum/client/types/sentence_chunker_config_request.py,sha256=EpGTP4z3YttiThYmdjwIBOI5YfsOlNP17dI9yiYqi3I,716
580
581
  vellum/client/types/sentence_chunking.py,sha256=guqU3072X4h8Laf6LhTWQ5lpjBpTgoXRxKp5iXJby2U,783
@@ -1610,7 +1611,7 @@ vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org
1610
1611
  vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=lGAWxTf02rRBgyxAFJNGldRX6sCRAZUDiUllg6zogHY,4459
1611
1612
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
1612
1613
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=fnjiRWLoRlC4Puo5oQcpZD5Hd-EesxsAo9l5tGAkpZQ,270
1613
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=bxCDBOF5HR17-YCSAqEkCJKYU50TYx1C15XaAb7Xxw8,12896
1614
+ vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=6zzgeBTUwCiYTLRhrZAqmi-nU2r4q0gX-ytnatY5kwY,12896
1614
1615
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1615
1616
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=H1xEaL3vgEPAGQaF_rt8kLl46qmVJEEhcORXT7oRdPk,22626
1616
1617
  vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=0a40fkkZkFMmZN0CsWf6EP_y1H6x36EGa3WcfVNyOsM,9797
@@ -1618,7 +1619,7 @@ vellum/workflows/nodes/displayable/bases/search_node.py,sha256=-BxQtuPgq8NZfUf5X
1618
1619
  vellum/workflows/nodes/displayable/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1619
1620
  vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMDPevgwLg1i6YK0g4L4bCy-7xCBN5yYZI,3156
1620
1621
  vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
1621
- vellum/workflows/nodes/displayable/bases/utils.py,sha256=ckMUenSsNkiYmSw6FmjSMHYaCk8Y8_sUjL6lkFFEqts,5412
1622
+ vellum/workflows/nodes/displayable/bases/utils.py,sha256=7L5wdQVm0ZMLpTJ7IBbTOzPhany7wSYPLYuY-0FBhvg,5490
1622
1623
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1623
1624
  vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=Qh4SPafzdRKbVKQH3h9SgH4vKeQQjvHAfP8ttUxLA2M,10304
1624
1625
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1668,11 +1669,11 @@ vellum/workflows/nodes/experimental/__init__.py,sha256=_tpZGWAZLydcKxfrj1-plrZeT
1668
1669
  vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
1669
1670
  vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=cKI2Ls25L-JVt4z4a2ozQa-YBeVy21Z7BQ32Sj7iBPE,10460
1670
1671
  vellum/workflows/nodes/experimental/tool_calling_node/__init__.py,sha256=S7OzT3I4cyOU5Beoz87nPwCejCMP2FsHBFL8OcVmxJ4,118
1671
- vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=CkSVbTHWX70MikBKMWfDRLb2xDhEl4K4qpIrNmRVVRU,5689
1672
+ vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=jwL1sbitmm1CpTOAEI0IIuc6VRr8d7yxUpS4Y5s9Bk8,5966
1672
1673
  vellum/workflows/nodes/experimental/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1673
- vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py,sha256=7x_o81vT7gWtVw3zDppcWnlJbakgxx_oI1esqhs2gpI,4551
1674
+ vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py,sha256=XK1H_QAT_nVFmFP442RYyPvpTfSgtU6kSGu3-OQPBNU,5072
1674
1675
  vellum/workflows/nodes/experimental/tool_calling_node/tests/test_utils.py,sha256=sF4ZfGK2uQNBVXC9yrnbFVzKIM-HWvXO1wak160MUTE,1386
1675
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=1wjOayk35rMEQWItpCUwP0UTtgdB27u1WzjsNAD0f4o,12052
1676
+ vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=ut3cdC7ezbxSvjMqHP0Km9yAVqOYoRztZVHShiq6zhk,12579
1676
1677
  vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
1677
1678
  vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1678
1679
  vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
@@ -1705,7 +1706,7 @@ vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2
1705
1706
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1706
1707
  vellum/workflows/state/base.py,sha256=WIMJYyuHUrP4zt0Nudk66HAK1L6GgGmsU_GQp7BGE2U,22189
1707
1708
  vellum/workflows/state/context.py,sha256=KOAI1wEGn8dGmhmAemJaf4SZbitP3jpIBcwKfznQaRE,3076
1708
- vellum/workflows/state/encoder.py,sha256=8NPQ8iz5qJeT5fafnZ2Pko98b-FtTjsgMNV4Zi3g2bE,2438
1709
+ vellum/workflows/state/encoder.py,sha256=I21qwYW3edIqlp78jdbjQgtou0hXYKvmofF-5_BCIlU,2552
1709
1710
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
1710
1711
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1711
1712
  vellum/workflows/state/tests/test_state.py,sha256=YOiC9qZAzkdiqb7nRarNWeDwxo7xHv3y3czlHl81ezg,6741
@@ -1715,18 +1716,18 @@ vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83y
1715
1716
  vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
1716
1717
  vellum/workflows/types/code_execution_node_wrappers.py,sha256=3MNIoFZKzVzNS5qFLVuDwMV17QJw72zo7NRf52yMq5A,3074
1717
1718
  vellum/workflows/types/core.py,sha256=iLJkMKf417kjwRncWdT_qsfJ-qBv5x58um7SfrydJbs,1266
1718
- vellum/workflows/types/definition.py,sha256=8DyNh3KP3p4Gsi-HJqai0f4sc9P-E2lDbp_0krsrE7g,2516
1719
+ vellum/workflows/types/definition.py,sha256=guuCHZkto8bkknoMcjfXRhSaDuiNjx3SNkutPf1makc,2506
1719
1720
  vellum/workflows/types/generics.py,sha256=tKXz0LwWJGKw1YGudyl9_yFDrRgU6yYV1yJV1Zv-LTw,1430
1720
1721
  vellum/workflows/types/stack.py,sha256=h7NE0vXR7l9DevFBIzIAk1Zh59K-kECQtDTKOUunwMY,1314
1721
1722
  vellum/workflows/types/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1722
1723
  vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
1723
1724
  vellum/workflows/types/utils.py,sha256=axxHbPLsnjhEOnMZrc5YarFd-P2bnsacBDQGNCvY8OY,6367
1724
1725
  vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1725
- vellum/workflows/utils/functions.py,sha256=eRtqQaxtv6NFe5v2pRO-b7kOH2_NOQS8tsKGOEEEdxY,7168
1726
+ vellum/workflows/utils/functions.py,sha256=W4t-IXvCoIn_oVY_wEkph1xjpd_xpGzBwExWqgJXeho,7168
1726
1727
  vellum/workflows/utils/names.py,sha256=QLUqfJ1tmSEeUwBKTTiv_Qk3QGbInC2RSmlXfGXc8Wo,380
1727
1728
  vellum/workflows/utils/pydantic_schema.py,sha256=eR_bBtY-T0pttJP-ARwagSdCOnwPUtiT3cegm2lzDTQ,1310
1728
1729
  vellum/workflows/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1729
- vellum/workflows/utils/tests/test_functions.py,sha256=bDobzPtxrFpkSBsR86BE4Zb8rD44y6tiTe6HXrLtOqU,18445
1730
+ vellum/workflows/utils/tests/test_functions.py,sha256=6AQt2Wu2Uj6UAxYzwi1Zd0YL4-ZOL47Yc_X3iE8ol9U,18445
1730
1731
  vellum/workflows/utils/tests/test_names.py,sha256=aOqpyvMsOEK_9mg_-yaNxQDW7QQfwqsYs37PseyLhxw,402
1731
1732
  vellum/workflows/utils/tests/test_uuids.py,sha256=i77ABQ0M3S-aFLzDXHJq_yr5FPkJEWCMBn1HJ3DObrE,437
1732
1733
  vellum/workflows/utils/tests/test_vellum_variables.py,sha256=vbnKgm41aB5OXlO-ZIPbhQ6xDiZkT8KMxCLqz4zocWY,1791
@@ -1739,8 +1740,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1739
1740
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1740
1741
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=fROqff6AZpCIzaSwOKSdtYy4XR0UZQ6ejxL3RJOSJVs,20447
1741
1742
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1742
- vellum_ai-0.14.73.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1743
- vellum_ai-0.14.73.dist-info/METADATA,sha256=XoU3nebnJNAQ7L7BEvZpXjoLwW0JMAUx88t4TikRG3s,5556
1744
- vellum_ai-0.14.73.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1745
- vellum_ai-0.14.73.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1746
- vellum_ai-0.14.73.dist-info/RECORD,,
1743
+ vellum_ai-0.14.74.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1744
+ vellum_ai-0.14.74.dist-info/METADATA,sha256=iFE4Twp7A5973aqOr498hlOQZ6LSnVAszYURwtB9VkI,5556
1745
+ vellum_ai-0.14.74.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1746
+ vellum_ai-0.14.74.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1747
+ vellum_ai-0.14.74.dist-info/RECORD,,
@@ -15,6 +15,20 @@ from vellum_ee.workflows.display.utils.expressions import serialize_value
15
15
  from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
16
16
  from vellum_ee.workflows.display.vellum import NodeInput
17
17
 
18
+
19
+ def _contains_descriptors(obj):
20
+ """Check if an object contains any descriptors or references that need special handling."""
21
+ if isinstance(obj, BaseDescriptor):
22
+ return True
23
+ elif isinstance(obj, dict):
24
+ return any(_contains_descriptors(v) for v in obj.values())
25
+ elif isinstance(obj, (list, tuple)):
26
+ return any(_contains_descriptors(item) for item in obj)
27
+ elif hasattr(obj, "__dict__"):
28
+ return any(_contains_descriptors(getattr(obj, field)) for field in obj.__dict__)
29
+ return False
30
+
31
+
18
32
  _InlinePromptNodeType = TypeVar("_InlinePromptNodeType", bound=InlinePromptNode)
19
33
 
20
34
 
@@ -23,7 +37,6 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
23
37
  InlinePromptNode.prompt_inputs,
24
38
  }
25
39
  __unserializable_attributes__ = {
26
- InlinePromptNode.parameters,
27
40
  InlinePromptNode.settings,
28
41
  InlinePromptNode.expand_meta,
29
42
  InlinePromptNode.request_options,
@@ -46,11 +59,15 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
46
59
 
47
60
  ml_model = str(raise_if_descriptor(node.ml_model))
48
61
 
49
- blocks: list = [
50
- self._generate_prompt_block(block, input_variable_id_by_name, [i])
51
- for i, block in enumerate(node_blocks)
52
- if not isinstance(block, BaseDescriptor)
53
- ]
62
+ has_descriptors = _contains_descriptors(node_blocks)
63
+
64
+ blocks: list = []
65
+ if not has_descriptors:
66
+ blocks = [
67
+ self._generate_prompt_block(block, input_variable_id_by_name, [i])
68
+ for i, block in enumerate(node_blocks)
69
+ if not isinstance(block, BaseDescriptor)
70
+ ]
54
71
 
55
72
  functions = (
56
73
  [self._generate_function_tools(function, i) for i, function in enumerate(function_definitions)]
@@ -72,7 +89,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
72
89
  "target_handle_id": str(self.get_target_handle_id()),
73
90
  "variant": "INLINE",
74
91
  "exec_config": {
75
- "parameters": raise_if_descriptor(node.parameters).dict(),
92
+ "parameters": self._serialize_parameters(node.parameters, display_context),
76
93
  "input_variables": [prompt_input.dict() for prompt_input in prompt_inputs],
77
94
  "prompt_template_block_data": {
78
95
  "version": 1,
@@ -156,6 +173,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
156
173
  }
157
174
 
158
175
  elif prompt_block.block_type == "CHAT_MESSAGE":
176
+
159
177
  chat_properties: JsonObject = {
160
178
  "chat_role": prompt_block.chat_role,
161
179
  "chat_source": prompt_block.chat_source,
@@ -253,3 +271,14 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
253
271
  raise ValueError(f"Failed to serialize attribute '{attribute.name}': {e}")
254
272
 
255
273
  return attributes
274
+
275
+ def _serialize_parameters(self, parameters, display_context: "WorkflowDisplayContext") -> JsonObject:
276
+ """Serialize parameters, returning empty object when nested descriptors are detected."""
277
+ params = raise_if_descriptor(parameters)
278
+ if not params:
279
+ return {}
280
+
281
+ if _contains_descriptors(params):
282
+ return {}
283
+
284
+ return params.dict()
@@ -2,6 +2,7 @@ import pytest
2
2
  from uuid import UUID
3
3
  from typing import Type
4
4
 
5
+ from vellum.client.types.prompt_parameters import PromptParameters
5
6
  from vellum.client.types.variable_prompt_block import VariablePromptBlock
6
7
  from vellum.workflows import BaseWorkflow
7
8
  from vellum.workflows.inputs import BaseInputs
@@ -280,3 +281,104 @@ def test_serialize_node__port_groups():
280
281
 
281
282
  # AND the legacy source_handle_id should be the default port
282
283
  assert my_prompt_node["data"]["source_handle_id"] == "149d97a4-3da3-44a9-95f7-ea7b8d38b877"
284
+
285
+
286
+ def test_serialize_node__prompt_parameters__dynamic_references():
287
+ # GIVEN input definition
288
+ class MyInputs(BaseInputs):
289
+ input_value: str
290
+
291
+ # AND a prompt node with PromptParameters containing dynamic references
292
+ class DynamicPromptNode(InlinePromptNode):
293
+ blocks = []
294
+ ml_model = "gpt-4o"
295
+ parameters = PromptParameters(custom_parameters={"json_schema": MyInputs.input_value})
296
+
297
+ # AND a workflow with the prompt node
298
+ class Workflow(BaseWorkflow[MyInputs, BaseState]):
299
+ graph = DynamicPromptNode
300
+
301
+ # WHEN the workflow is serialized
302
+ workflow_display = get_workflow_display(workflow_class=Workflow)
303
+ serialized_workflow: dict = workflow_display.serialize()
304
+
305
+ # THEN the node should properly serialize the PromptParameters
306
+ dynamic_prompt_node = next(
307
+ node
308
+ for node in serialized_workflow["workflow_raw_data"]["nodes"]
309
+ if node["id"] == str(DynamicPromptNode.__id__)
310
+ )
311
+
312
+ # AND the parameters should be properly serialized in exec_config
313
+ exec_config = dynamic_prompt_node["data"]["exec_config"]
314
+ assert "parameters" in exec_config
315
+
316
+ parameters = exec_config["parameters"]
317
+ assert parameters == {}
318
+
319
+ # AND the parameters should also be serialized in the attributes array
320
+ parameters_attribute = next(
321
+ (attr for attr in dynamic_prompt_node.get("attributes", []) if attr["name"] == "parameters"), None
322
+ )
323
+ assert parameters_attribute is not None
324
+ assert parameters_attribute["name"] == "parameters"
325
+ assert parameters_attribute["value"]["type"] == "DICTIONARY_REFERENCE"
326
+ assert parameters_attribute["value"]["entries"] == [
327
+ {
328
+ "id": "6b63ff96-a2eb-4c6e-bad1-bde01605fa86",
329
+ "key": "stop",
330
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
331
+ },
332
+ {
333
+ "id": "265a1c17-2089-4ac1-b2ce-361b6b9a3335",
334
+ "key": "temperature",
335
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
336
+ },
337
+ {
338
+ "id": "699976ec-8ec2-476a-a011-7cf810a8a307",
339
+ "key": "max_tokens",
340
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
341
+ },
342
+ {
343
+ "id": "a87e23da-9794-41ff-ba80-c3a77e976e75",
344
+ "key": "top_p",
345
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
346
+ },
347
+ {
348
+ "id": "18eb53c2-ec1a-4115-9f21-083af430df67",
349
+ "key": "top_k",
350
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
351
+ },
352
+ {
353
+ "id": "295509a2-5837-452c-893d-f47b67c63c8a",
354
+ "key": "frequency_penalty",
355
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
356
+ },
357
+ {
358
+ "id": "5fc64379-5566-426a-a909-dd56c3305aa5",
359
+ "key": "presence_penalty",
360
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
361
+ },
362
+ {
363
+ "id": "5d326da0-c096-4425-8bf1-3a18764e96e3",
364
+ "key": "logit_bias",
365
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
366
+ },
367
+ {
368
+ "id": "cd1a0e1b-6667-48a0-9964-257e1ec8851d",
369
+ "key": "custom_parameters",
370
+ "value": {
371
+ "entries": [
372
+ {
373
+ "id": "a9a3092e-dd18-4533-b6b5-24588ebd8f7f",
374
+ "key": "json_schema",
375
+ "value": {
376
+ "input_variable_id": "c02d1201-86d1-4364-b3b3-4fc6824db8a4",
377
+ "type": "WORKFLOW_INPUT",
378
+ },
379
+ }
380
+ ],
381
+ "type": "DICTIONARY_REFERENCE",
382
+ },
383
+ },
384
+ ]
@@ -1,7 +1,10 @@
1
+ import pytest
2
+ from dataclasses import dataclass
1
3
  from uuid import uuid4
2
4
  from typing import List
3
5
 
4
6
  from deepdiff import DeepDiff
7
+ from pydantic import BaseModel
5
8
 
6
9
  from vellum.client.types.chat_message import ChatMessage
7
10
  from vellum.workflows.inputs.base import BaseInputs
@@ -70,6 +73,28 @@ def test_serialize_node__constant_value(serialize_node):
70
73
  )
71
74
 
72
75
 
76
+ @pytest.mark.parametrize(
77
+ "boolean_value, expected_value",
78
+ [
79
+ (True, True),
80
+ (False, False),
81
+ ],
82
+ )
83
+ def test_serialize_node__constant_boolean_value(serialize_node, boolean_value, expected_value):
84
+ class BooleanValueGenericNode(BaseNode):
85
+ attr: bool = boolean_value
86
+
87
+ serialized_node = serialize_node(BooleanValueGenericNode)
88
+
89
+ assert serialized_node["attributes"][0]["value"] == {
90
+ "type": "CONSTANT_VALUE",
91
+ "value": {
92
+ "type": "JSON",
93
+ "value": expected_value,
94
+ },
95
+ }
96
+
97
+
73
98
  def test_serialize_node__constant_value_reference(serialize_node):
74
99
  class ConstantValueReferenceGenericNode(BaseNode):
75
100
  attr: str = ConstantValueReference("hello")
@@ -580,3 +605,68 @@ def test_serialize_node__coalesce(serialize_node):
580
605
  serialized_node,
581
606
  ignore_order=True,
582
607
  )
608
+
609
+
610
+ def test_serialize_node__dataclass_with_node_output_reference(serialize_node):
611
+ @dataclass
612
+ class MyDataClass:
613
+ name: str
614
+ node_ref: str
615
+
616
+ class NodeWithOutput(BaseNode):
617
+ class Outputs(BaseNode.Outputs):
618
+ result: str
619
+
620
+ class NodeWithOutputDisplay(BaseNodeDisplay[NodeWithOutput]):
621
+ pass
622
+
623
+ class GenericNodeWithDataclass(BaseNode):
624
+ attr = MyDataClass(name="test", node_ref=NodeWithOutput.Outputs.result)
625
+
626
+ node_output_id = uuid4()
627
+ serialized_node = serialize_node(
628
+ node_class=GenericNodeWithDataclass,
629
+ global_node_displays={NodeWithOutput: NodeWithOutputDisplay()},
630
+ global_node_output_displays={
631
+ NodeWithOutput.Outputs.result: (NodeWithOutput, NodeOutputDisplay(id=node_output_id, name="result"))
632
+ },
633
+ )
634
+
635
+ attr_value = serialized_node["attributes"][0]["value"]
636
+ assert attr_value["type"] == "DICTIONARY_REFERENCE"
637
+
638
+ assert any(
639
+ entry["key"] == "node_ref" and entry["value"]["type"] == "NODE_OUTPUT" for entry in attr_value["entries"]
640
+ )
641
+
642
+
643
+ def test_serialize_node__pydantic_with_node_output_reference(serialize_node):
644
+ class MyPydanticModel(BaseModel):
645
+ name: str
646
+ node_ref: str
647
+
648
+ class NodeWithOutput(BaseNode):
649
+ class Outputs(BaseNode.Outputs):
650
+ result: str
651
+
652
+ class NodeWithOutputDisplay(BaseNodeDisplay[NodeWithOutput]):
653
+ pass
654
+
655
+ class GenericNodeWithPydantic(BaseNode):
656
+ attr = MyPydanticModel(name="test", node_ref=NodeWithOutput.Outputs.result)
657
+
658
+ node_output_id = uuid4()
659
+ serialized_node = serialize_node(
660
+ node_class=GenericNodeWithPydantic,
661
+ global_node_displays={NodeWithOutput: NodeWithOutputDisplay()},
662
+ global_node_output_displays={
663
+ NodeWithOutput.Outputs.result: (NodeWithOutput, NodeOutputDisplay(id=node_output_id, name="result"))
664
+ },
665
+ )
666
+
667
+ attr_value = serialized_node["attributes"][0]["value"]
668
+ assert attr_value["type"] == "DICTIONARY_REFERENCE"
669
+
670
+ assert any(
671
+ entry["key"] == "node_ref" and entry["value"]["type"] == "NODE_OUTPUT" for entry in attr_value["entries"]
672
+ )
@@ -240,6 +240,27 @@ def test_serialize_workflow():
240
240
  },
241
241
  },
242
242
  },
243
+ {
244
+ "id": "2b98319f-f43d-42d9-a8b0-b148d5de0a2c",
245
+ "name": "parameters",
246
+ "value": {
247
+ "type": "CONSTANT_VALUE",
248
+ "value": {
249
+ "type": "JSON",
250
+ "value": {
251
+ "stop": [],
252
+ "temperature": 0.0,
253
+ "max_tokens": 4096,
254
+ "top_p": 1.0,
255
+ "top_k": 0,
256
+ "frequency_penalty": 0.0,
257
+ "presence_penalty": 0.0,
258
+ "logit_bias": None,
259
+ "custom_parameters": None,
260
+ },
261
+ },
262
+ },
263
+ },
243
264
  ],
244
265
  },
245
266
  prompt_node,
@@ -437,3 +458,99 @@ def test_serialize_workflow_with_descriptor_blocks():
437
458
  },
438
459
  }
439
460
  ]
461
+
462
+
463
+ def test_serialize_workflow_with_nested_descriptor_blocks():
464
+ """Test that serialization handles BaseDescriptor instances nested in ChatMessageBlock.blocks."""
465
+
466
+ class TestInputs(BaseInputs):
467
+ noun: str
468
+
469
+ class UpstreamNode(BaseNode):
470
+ class Outputs(BaseNode.Outputs):
471
+ results: list
472
+
473
+ def run(self) -> Outputs:
474
+ return self.Outputs(results=["test"])
475
+
476
+ chat_block = ChatMessagePromptBlock(chat_role="SYSTEM", blocks=[JinjaPromptBlock(template="Hello")])
477
+
478
+ class TestInlinePromptNodeWithNestedDescriptorBlocks(InlinePromptNode):
479
+ ml_model = "gpt-4o"
480
+ blocks = [chat_block]
481
+ prompt_inputs = {"noun": TestInputs.noun}
482
+
483
+ object.__setattr__(chat_block, "blocks", [UpstreamNode.Outputs.results[0]])
484
+
485
+ class TestWorkflow(BaseWorkflow[TestInputs, BaseState]):
486
+ graph = UpstreamNode >> TestInlinePromptNodeWithNestedDescriptorBlocks
487
+
488
+ workflow_display = get_workflow_display(workflow_class=TestWorkflow)
489
+ serialized: dict = workflow_display.serialize()
490
+
491
+ prompt_nodes = [node for node in serialized["workflow_raw_data"]["nodes"] if node["type"] == "PROMPT"]
492
+ prompt_node = prompt_nodes[0]
493
+
494
+ blocks = prompt_node["data"]["exec_config"]["prompt_template_block_data"]["blocks"]
495
+ descriptor_blocks = [block for block in blocks if not isinstance(block, dict) or not block.get("block_type")]
496
+ assert len(descriptor_blocks) == 0, "BaseDescriptor blocks should not appear in serialized blocks"
497
+
498
+ blocks_attr = next((attr for attr in prompt_node["attributes"] if attr["name"] == "blocks"), None)
499
+ assert blocks_attr is not None, "blocks attribute should be present when blocks contain nested BaseDescriptor"
500
+ assert blocks_attr["value"]["type"] == "ARRAY_REFERENCE", "blocks attribute should be serialized as ARRAY_REFERENCE"
501
+ assert blocks_attr["value"]["items"] == [
502
+ {
503
+ "entries": [
504
+ {
505
+ "id": "24a203be-3cba-4b20-bc84-9993a476c120",
506
+ "key": "block_type",
507
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "CHAT_MESSAGE"}},
508
+ },
509
+ {
510
+ "id": "c06269e6-f74c-4860-8fa5-22dcbdc89399",
511
+ "key": "state",
512
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
513
+ },
514
+ {
515
+ "id": "dd9c0d43-b931-4dc8-8b3a-a7507ddff0c1",
516
+ "key": "cache_config",
517
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
518
+ },
519
+ {
520
+ "id": "bef22f2b-0b6e-4910-88cc-6df736d2e20e",
521
+ "key": "chat_role",
522
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "SYSTEM"}},
523
+ },
524
+ {
525
+ "id": "c0beec30-f85e-4a78-a3fb-baee54a692f8",
526
+ "key": "chat_source",
527
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
528
+ },
529
+ {
530
+ "id": "f601f4f2-62fe-4697-9fe0-99ca8aa64500",
531
+ "key": "chat_message_unterminated",
532
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
533
+ },
534
+ {
535
+ "id": "ad550008-64e3-44a3-a32a-84ec226db31c",
536
+ "key": "blocks",
537
+ "value": {
538
+ "items": [
539
+ {
540
+ "lhs": {
541
+ "node_id": "9fe5d3a3-7d26-4692-aa2d-e67c673b0c2b",
542
+ "node_output_id": "92f9a1b7-d33b-4f00-b4c2-e6f58150e166",
543
+ "type": "NODE_OUTPUT",
544
+ },
545
+ "operator": "accessField",
546
+ "rhs": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 0.0}},
547
+ "type": "BINARY_EXPRESSION",
548
+ }
549
+ ],
550
+ "type": "ARRAY_REFERENCE",
551
+ },
552
+ },
553
+ ],
554
+ "type": "DICTIONARY_REFERENCE",
555
+ }
556
+ ]
@@ -131,6 +131,8 @@ def test_serialize_workflow():
131
131
  "value": [
132
132
  {
133
133
  "type": "INLINE_WORKFLOW",
134
+ "name": "BasicInlineSubworkflowWorkflow",
135
+ "description": "\n A workflow that gets the weather for a given city and date.\n ", # noqa: E501
134
136
  "exec_config": {
135
137
  "workflow_raw_data": {
136
138
  "nodes": [
@@ -410,6 +412,11 @@ def test_serialize_workflow():
410
412
  "name": "function_configs",
411
413
  "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
412
414
  },
415
+ {
416
+ "id": "1668419e-a193-43a5-8a97-3394e89bf278",
417
+ "name": "max_prompt_iterations",
418
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 5.0}},
419
+ },
413
420
  ],
414
421
  "outputs": [
415
422
  {"id": "e62bc785-a914-4066-b79e-8c89a5d0ec6c", "name": "text", "type": "STRING", "value": None},
@@ -131,6 +131,8 @@ def test_serialize_workflow():
131
131
  "value": [
132
132
  {
133
133
  "type": "CODE_EXECUTION",
134
+ "name": "get_current_weather",
135
+ "description": "\n Get the current weather in a given location.\n ",
134
136
  "definition": {
135
137
  "state": None,
136
138
  "cache_config": None,
@@ -183,6 +185,11 @@ def test_serialize_workflow():
183
185
  ],
184
186
  },
185
187
  },
188
+ {
189
+ "id": "1668419e-a193-43a5-8a97-3394e89bf278",
190
+ "name": "max_prompt_iterations",
191
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 5.0}},
192
+ },
186
193
  ],
187
194
  "outputs": [
188
195
  {"id": "e62bc785-a914-4066-b79e-8c89a5d0ec6c", "name": "text", "type": "STRING", "value": None},
@@ -0,0 +1,62 @@
1
+ from deepdiff import DeepDiff
2
+
3
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
4
+
5
+ from tests.workflows.basic_tool_calling_node_workflow_deployment.workflow import (
6
+ BasicToolCallingNodeWorkflowDeploymentWorkflow,
7
+ )
8
+
9
+
10
+ def test_serialize_workflow():
11
+ # GIVEN a Workflow that uses a generic node
12
+ # WHEN we serialize it
13
+ workflow_display = get_workflow_display(workflow_class=BasicToolCallingNodeWorkflowDeploymentWorkflow)
14
+
15
+ serialized_workflow: dict = workflow_display.serialize()
16
+ # THEN we should get a serialized representation of the Workflow
17
+ assert serialized_workflow.keys() == {
18
+ "workflow_raw_data",
19
+ "input_variables",
20
+ "state_variables",
21
+ "output_variables",
22
+ }
23
+
24
+ # AND its input variables should be what we expect
25
+ input_variables = serialized_workflow["input_variables"]
26
+ assert len(input_variables) == 1
27
+
28
+ # AND its output variables should be what we expect
29
+ output_variables = serialized_workflow["output_variables"]
30
+ assert len(output_variables) == 2
31
+ assert not DeepDiff(
32
+ [
33
+ {"id": "3626cfa6-76f8-465e-b156-c809e3a2cee9", "key": "text", "type": "STRING"},
34
+ {"id": "cb82117a-d649-4c3e-9342-c552028fa2ad", "key": "chat_history", "type": "CHAT_HISTORY"},
35
+ ],
36
+ output_variables,
37
+ ignore_order=True,
38
+ )
39
+
40
+ # AND its raw data should be what we expect
41
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
42
+ tool_calling_node = workflow_raw_data["nodes"][1]
43
+ function_attributes = next(attr for attr in tool_calling_node["attributes"] if attr["name"] == "functions")
44
+ assert function_attributes == {
45
+ "id": "73a94e3c-1935-4308-a68a-ecd5441804b7",
46
+ "name": "functions",
47
+ "value": {
48
+ "type": "CONSTANT_VALUE",
49
+ "value": {
50
+ "type": "JSON",
51
+ "value": [
52
+ {
53
+ "type": "WORKFLOW_DEPLOYMENT",
54
+ "name": "deployment_1",
55
+ "description": "Workflow deployment for deployment_1",
56
+ "deployment": "deployment_1",
57
+ "release_tag": "LATEST",
58
+ }
59
+ ],
60
+ },
61
+ },
62
+ }
@@ -1,5 +1,8 @@
1
+ from dataclasses import asdict, is_dataclass
1
2
  from typing import TYPE_CHECKING, Any, Dict, List, cast
2
3
 
4
+ from pydantic import BaseModel
5
+
3
6
  from vellum.client.types.logical_operator import LogicalOperator
4
7
  from vellum.workflows.descriptors.base import BaseDescriptor
5
8
  from vellum.workflows.expressions.accessor import AccessorExpression
@@ -39,6 +42,7 @@ from vellum.workflows.references.state_value import StateValueReference
39
42
  from vellum.workflows.references.vellum_secret import VellumSecretReference
40
43
  from vellum.workflows.references.workflow_input import WorkflowInputReference
41
44
  from vellum.workflows.types.core import JsonArray, JsonObject
45
+ from vellum.workflows.types.definition import DeploymentDefinition
42
46
  from vellum.workflows.types.generics import is_workflow_class
43
47
  from vellum.workflows.utils.uuids import uuid4_from_hash
44
48
  from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
@@ -290,6 +294,10 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
290
294
  "items": cast(JsonArray, serialized_items), # list[JsonObject] -> JsonArray
291
295
  }
292
296
 
297
+ if is_dataclass(value) and not isinstance(value, type):
298
+ dict_value = asdict(value)
299
+ return serialize_value(display_context, dict_value)
300
+
293
301
  if isinstance(value, dict):
294
302
  serialized_entries: List[Dict[str, Any]] = [
295
303
  {
@@ -321,18 +329,41 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
321
329
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
322
330
 
323
331
  workflow_display = get_workflow_display(workflow_class=value)
324
- value = workflow_display.serialize()
332
+ serialized_value: dict = workflow_display.serialize()
333
+ name = serialized_value["workflow_raw_data"]["definition"]["name"]
334
+ description = value.__doc__ or ""
325
335
  return {
326
336
  "type": "CONSTANT_VALUE",
327
337
  "value": {
328
338
  "type": "JSON",
329
339
  "value": {
330
340
  "type": "INLINE_WORKFLOW",
331
- "exec_config": value,
341
+ "name": name,
342
+ "description": description,
343
+ "exec_config": serialized_value,
344
+ },
345
+ },
346
+ }
347
+
348
+ if isinstance(value, DeploymentDefinition):
349
+ return {
350
+ "type": "CONSTANT_VALUE",
351
+ "value": {
352
+ "type": "JSON",
353
+ "value": {
354
+ "type": "WORKFLOW_DEPLOYMENT",
355
+ "name": value.deployment,
356
+ "description": f"Workflow deployment for {value.deployment}",
357
+ "deployment": value.deployment,
358
+ "release_tag": value.release_tag,
332
359
  },
333
360
  },
334
361
  }
335
362
 
363
+ if isinstance(value, BaseModel):
364
+ dict_value = value.model_dump()
365
+ return serialize_value(display_context, dict_value)
366
+
336
367
  if not isinstance(value, BaseDescriptor):
337
368
  vellum_value = primitive_to_vellum_value(value)
338
369
  return {
@@ -690,8 +690,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
690
690
  try:
691
691
  display_module = importlib.import_module(full_workflow_display_module_path)
692
692
  except ModuleNotFoundError:
693
- logger.exception("Failed to import workflow display module: %s", full_workflow_display_module_path)
694
- return None
693
+ return BaseWorkflowDisplay._gather_event_display_context_from_workflow_crawling(module_path, workflow_class)
695
694
 
696
695
  WorkflowDisplayClass: Optional[Type[BaseWorkflowDisplay]] = None
697
696
  for name, definition in display_module.__dict__.items():
@@ -708,11 +707,26 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
708
707
  WorkflowDisplayClass = definition
709
708
  break
710
709
 
711
- if not WorkflowDisplayClass:
712
- logger.exception("No workflow display class found in module: %s", full_workflow_display_module_path)
713
- return None
710
+ if WorkflowDisplayClass:
711
+ return WorkflowDisplayClass().get_event_display_context()
712
+
713
+ return BaseWorkflowDisplay._gather_event_display_context_from_workflow_crawling(module_path, workflow_class)
714
+
715
+ @staticmethod
716
+ def _gather_event_display_context_from_workflow_crawling(
717
+ module_path: str,
718
+ workflow_class: Optional[Type[BaseWorkflow]] = None,
719
+ ) -> Union[WorkflowEventDisplayContext, None]:
720
+ try:
721
+ if workflow_class is None:
722
+ workflow_class = BaseWorkflow.load_from_module(module_path)
714
723
 
715
- return WorkflowDisplayClass().get_event_display_context()
724
+ workflow_display = get_workflow_display(workflow_class=workflow_class)
725
+ return workflow_display.get_event_display_context()
726
+
727
+ except ModuleNotFoundError:
728
+ logger.exception("Failed to load workflow from module %s", module_path)
729
+ return None
716
730
 
717
731
  def get_event_display_context(self):
718
732
  display_context = self.display_context
@@ -4,6 +4,7 @@ import sys
4
4
  from uuid import uuid4
5
5
 
6
6
  from vellum.workflows import BaseWorkflow
7
+ from vellum.workflows.events.workflow import WorkflowEventDisplayContext
7
8
  from vellum_ee.workflows.display.workflows import BaseWorkflowDisplay
8
9
  from vellum_ee.workflows.server.virtual_file_loader import VirtualFileFinder
9
10
 
@@ -102,3 +103,43 @@ class MyCustomWorkflowDisplay(BaseWorkflowDisplay[MyCustomWorkflow]):
102
103
  assert display_meta.workflow_outputs == {
103
104
  "answer": workflow_output_id,
104
105
  }
106
+
107
+
108
+ def test_gather_event_display_context__workflow_crawling_without_display_module():
109
+ # GIVEN a workflow module without a display module
110
+ files = {
111
+ "__init__.py": "",
112
+ "workflow.py": """\
113
+ from vellum.workflows import BaseWorkflow
114
+ from vellum.workflows.nodes import BaseNode
115
+
116
+ class TestNode(BaseNode):
117
+ class Outputs(BaseNode.Outputs):
118
+ result: str
119
+
120
+ class TestWorkflow(BaseWorkflow):
121
+ graph = TestNode
122
+
123
+ class Outputs(BaseWorkflow.Outputs):
124
+ final_result = TestNode.Outputs.result
125
+ """,
126
+ }
127
+
128
+ namespace = str(uuid4())
129
+
130
+ # AND the virtual file loader is registered
131
+ sys.meta_path.append(VirtualFileFinder(files, namespace))
132
+
133
+ # WHEN the workflow display context is gathered
134
+ display_meta = BaseWorkflowDisplay.gather_event_display_context(namespace)
135
+
136
+ # THEN the workflow display context should be successfully created via workflow crawling
137
+ assert display_meta is not None
138
+ assert isinstance(display_meta, WorkflowEventDisplayContext)
139
+
140
+ # AND the node displays should be populated with the correct node structure
141
+ assert len(display_meta.node_displays) == 1
142
+ node_display = list(display_meta.node_displays.values())[0]
143
+ assert "result" in node_display.output_display
144
+
145
+ assert "final_result" in display_meta.workflow_outputs