vellum-ai 0.14.62__py3-none-any.whl → 0.14.64__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 (23) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/errors/types.py +11 -1
  3. vellum/workflows/nodes/bases/base.py +3 -3
  4. vellum/workflows/nodes/bases/base_adornment_node.py +2 -0
  5. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +4 -0
  6. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +4 -0
  7. vellum/workflows/nodes/displayable/code_execution_node/node.py +5 -1
  8. vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +68 -2
  9. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +4 -0
  10. vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py +25 -0
  11. vellum/workflows/nodes/experimental/tool_calling_node/node.py +25 -8
  12. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +101 -29
  13. vellum/workflows/types/code_execution_node_wrappers.py +0 -4
  14. {vellum_ai-0.14.62.dist-info → vellum_ai-0.14.64.dist-info}/METADATA +11 -12
  15. {vellum_ai-0.14.62.dist-info → vellum_ai-0.14.64.dist-info}/RECORD +23 -23
  16. vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -1
  17. vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +100 -0
  18. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +16 -5
  19. vellum_ee/workflows/display/utils/expressions.py +13 -3
  20. /vellum/workflows/nodes/experimental/tool_calling_node/tests/{test_tool_calling_node.py → test_node.py} +0 -0
  21. {vellum_ai-0.14.62.dist-info → vellum_ai-0.14.64.dist-info}/LICENSE +0 -0
  22. {vellum_ai-0.14.62.dist-info → vellum_ai-0.14.64.dist-info}/WHEEL +0 -0
  23. {vellum_ai-0.14.62.dist-info → vellum_ai-0.14.64.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.62",
21
+ "X-Fern-SDK-Version": "0.14.64",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -1,5 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum
3
+ import logging
3
4
  from typing import Any, Dict
4
5
 
5
6
  from vellum.client.types.vellum_error import VellumError
@@ -7,6 +8,8 @@ from vellum.client.types.vellum_error_code_enum import VellumErrorCodeEnum
7
8
  from vellum.client.types.workflow_event_error import WorkflowEventError
8
9
  from vellum.client.types.workflow_execution_event_error_code import WorkflowExecutionEventErrorCode
9
10
 
11
+ logger = logging.getLogger(__name__)
12
+
10
13
 
11
14
  class WorkflowErrorCode(Enum):
12
15
  INVALID_WORKFLOW = "INVALID_WORKFLOW"
@@ -43,9 +46,15 @@ _VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE: Dict[VellumErrorCodeEnum, WorkflowErr
43
46
 
44
47
 
45
48
  def vellum_error_to_workflow_error(error: VellumError) -> WorkflowError:
49
+ workflow_error_code = _VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE.get(error.code)
50
+
51
+ if workflow_error_code is None:
52
+ logger.warning(f"Unknown Vellum error code '{error.code}' encountered. Falling back to INTERNAL_ERROR.")
53
+ workflow_error_code = WorkflowErrorCode.INTERNAL_ERROR
54
+
46
55
  return WorkflowError(
47
56
  message=error.message,
48
- code=_VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE.get(error.code, WorkflowErrorCode.INTERNAL_ERROR),
57
+ code=workflow_error_code,
49
58
  )
50
59
 
51
60
 
@@ -57,6 +66,7 @@ _WORKFLOW_EVENT_ERROR_CODE_TO_WORKFLOW_ERROR_CODE: Dict[WorkflowExecutionEventEr
57
66
  "NODE_EXECUTION": WorkflowErrorCode.NODE_EXECUTION,
58
67
  "LLM_PROVIDER": WorkflowErrorCode.PROVIDER_ERROR,
59
68
  "INVALID_CODE": WorkflowErrorCode.INVALID_CODE,
69
+ "INVALID_INPUTS": WorkflowErrorCode.INVALID_INPUTS,
60
70
  "INVALID_TEMPLATE": WorkflowErrorCode.INVALID_TEMPLATE,
61
71
  "USER_DEFINED_ERROR": WorkflowErrorCode.USER_DEFINED_ERROR,
62
72
  }
@@ -1,3 +1,4 @@
1
+ from abc import ABC, ABCMeta
1
2
  from dataclasses import field
2
3
  from functools import cached_property, reduce
3
4
  import inspect
@@ -50,7 +51,7 @@ def _is_annotated(cls: Type, name: str) -> bool:
50
51
  return False
51
52
 
52
53
 
53
- class BaseNodeMeta(type):
54
+ class BaseNodeMeta(ABCMeta):
54
55
  def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
55
56
  if "Outputs" in dct:
56
57
  outputs_class = dct["Outputs"]
@@ -258,7 +259,7 @@ class _BaseNodeExecutionMeta(type):
258
259
  NodeRunResponse = Union[BaseOutputs, Iterator[BaseOutput]]
259
260
 
260
261
 
261
- class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
262
+ class BaseNode(Generic[StateType], ABC, metaclass=BaseNodeMeta):
262
263
  __id__: UUID = uuid4_from_hash(__qualname__)
263
264
  __output_ids__: Dict[str, UUID] = {}
264
265
  state: StateType
@@ -490,7 +491,6 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
490
491
  node_output_descriptor.name,
491
492
  node_output_descriptor.instance.resolve(self.state),
492
493
  )
493
- delattr(self.Outputs, "_outputs_post_init")
494
494
 
495
495
  setattr(self.Outputs, "_outputs_post_init", _outputs_post_init)
496
496
 
@@ -1,3 +1,4 @@
1
+ from abc import ABC
1
2
  from typing import TYPE_CHECKING, Any, Dict, Generic, Optional, Tuple, Type
2
3
 
3
4
  from vellum.workflows.inputs.base import BaseInputs
@@ -67,6 +68,7 @@ class _BaseAdornmentNodeMeta(BaseNodeMeta):
67
68
  class BaseAdornmentNode(
68
69
  BaseNode[StateType],
69
70
  Generic[StateType],
71
+ ABC,
70
72
  metaclass=_BaseAdornmentNodeMeta,
71
73
  ):
72
74
  """
@@ -193,6 +193,10 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
193
193
  return input_variables, input_values
194
194
 
195
195
  for input_name, input_value in self.prompt_inputs.items():
196
+ # Exclude inputs that resolved to be null. This ensure that we don't pass input values
197
+ # to optional prompt inputs whose values were unresolved.
198
+ if input_value is None:
199
+ continue
196
200
  if isinstance(input_value, str):
197
201
  input_variables.append(
198
202
  VellumVariable(
@@ -171,6 +171,10 @@ class BasePromptDeploymentNode(BasePromptNode, Generic[StateType]):
171
171
  return compiled_inputs
172
172
 
173
173
  for input_name, input_value in self.prompt_inputs.items():
174
+ # Exclude inputs that resolved to be null. This ensure that we don't pass input values
175
+ # to optional prompt inputs whose values were unresolved.
176
+ if input_value is None:
177
+ continue
174
178
  if isinstance(input_value, str):
175
179
  compiled_inputs.append(
176
180
  StringInputRequest(
@@ -27,6 +27,7 @@ from vellum.workflows.errors.types import WorkflowErrorCode
27
27
  from vellum.workflows.exceptions import NodeException
28
28
  from vellum.workflows.nodes.bases import BaseNode
29
29
  from vellum.workflows.nodes.bases.base import BaseNodeMeta
30
+ from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
30
31
  from vellum.workflows.nodes.displayable.code_execution_node.utils import read_file_from_path, run_code_inline
31
32
  from vellum.workflows.outputs.base import BaseOutputs
32
33
  from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior, VellumSecret
@@ -169,10 +170,13 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
169
170
  )
170
171
  )
171
172
  else:
173
+ # Convert primitive values to VellumValue objects
174
+ vellum_values: List[VellumValue] = [primitive_to_vellum_value(item) for item in input_value]
175
+
172
176
  compiled_inputs.append(
173
177
  ArrayInput(
174
178
  name=input_name,
175
- value=cast(List[VellumValue], input_value),
179
+ value=vellum_values,
176
180
  )
177
181
  )
178
182
  elif isinstance(input_value, dict):
@@ -5,7 +5,7 @@ from typing import Any, List, Union
5
5
 
6
6
  from pydantic import BaseModel
7
7
 
8
- from vellum import CodeExecutorResponse, NumberVellumValue, StringInput, StringVellumValue
8
+ from vellum import ArrayInput, CodeExecutorResponse, NumberVellumValue, StringInput, StringVellumValue
9
9
  from vellum.client.errors.bad_request_error import BadRequestError
10
10
  from vellum.client.types.chat_message import ChatMessage
11
11
  from vellum.client.types.code_execution_package import CodeExecutionPackage
@@ -1099,7 +1099,7 @@ def test_run_node__iter_dict():
1099
1099
  class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, list[str]]):
1100
1100
  code = """\
1101
1101
  def main(input_dict: dict) -> list[str]:
1102
- return [item.value for item in input_dict]
1102
+ return [item.value for item in input_dict.values()]
1103
1103
  """
1104
1104
  runtime = "PYTHON_3_11_6"
1105
1105
  code_inputs = {
@@ -1167,3 +1167,69 @@ def main(input: str) -> str:
1167
1167
 
1168
1168
  # THEN the node should return default function call
1169
1169
  assert outputs == {"result": FunctionCall(name="", arguments={}), "log": ""}
1170
+
1171
+
1172
+ def test_run_node__array_input_with_primitive_values(vellum_client):
1173
+ # GIVEN a node that subclasses CodeExecutionNode that processes an array of primitive values
1174
+ class State(BaseState):
1175
+ pass
1176
+
1177
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, str]):
1178
+ code = """\
1179
+ from typing import List
1180
+ def main(numbers: List[str]) -> str:
1181
+ return " ".join(numbers)
1182
+ """
1183
+ runtime = "PYTHON_3_11_6"
1184
+ packages = [
1185
+ CodeExecutionPackage(
1186
+ name="openai",
1187
+ version="1.0.0",
1188
+ )
1189
+ ]
1190
+
1191
+ code_inputs = {
1192
+ "numbers": ["6", "4", "2"],
1193
+ }
1194
+
1195
+ # AND we know what the Code Execution Node will respond with
1196
+ mock_code_execution = CodeExecutorResponse(
1197
+ log="",
1198
+ output=StringVellumValue(value="6 4 2"),
1199
+ )
1200
+ vellum_client.execute_code.return_value = mock_code_execution
1201
+
1202
+ # WHEN we run the node
1203
+ node = ExampleCodeExecutionNode(state=State())
1204
+ outputs = node.run()
1205
+
1206
+ # THEN the node should have produced the outputs we expect
1207
+ assert outputs == {"result": "6 4 2", "log": ""}
1208
+
1209
+ # AND we should have invoked the Code with the expected inputs
1210
+ vellum_client.execute_code.assert_called_once_with(
1211
+ input_values=[
1212
+ ArrayInput(
1213
+ name="numbers",
1214
+ value=[
1215
+ StringVellumValue(value="6"),
1216
+ StringVellumValue(value="4"),
1217
+ StringVellumValue(value="2"),
1218
+ ],
1219
+ )
1220
+ ],
1221
+ code="""\
1222
+ from typing import List
1223
+ def main(numbers: List[str]) -> str:
1224
+ return " ".join(numbers)
1225
+ """,
1226
+ runtime="PYTHON_3_11_6",
1227
+ output_type="STRING",
1228
+ packages=[
1229
+ CodeExecutionPackage(
1230
+ name="openai",
1231
+ version="1.0.0",
1232
+ )
1233
+ ],
1234
+ request_options=None,
1235
+ )
@@ -63,6 +63,10 @@ class SubworkflowDeploymentNode(BaseNode[StateType], Generic[StateType]):
63
63
  compiled_inputs: List[WorkflowRequestInputRequest] = []
64
64
 
65
65
  for input_name, input_value in self.subworkflow_inputs.items():
66
+ # Exclude inputs that resolved to be null. This ensure that we don't pass input values
67
+ # to optional subworkflow inputs whose values were unresolved.
68
+ if input_value is None:
69
+ continue
66
70
  if isinstance(input_value, str):
67
71
  compiled_inputs.append(
68
72
  WorkflowRequestStringInputRequest(
@@ -468,3 +468,28 @@ def test_prompt_deployment_node__parent_context_serialization(mock_httpx_transpo
468
468
  request_execution_context = json.loads(call_request_args.read().decode("utf-8"))["execution_context"]
469
469
  assert request_execution_context["trace_id"] == str(trace_id)
470
470
  assert request_execution_context["parent_context"]
471
+
472
+
473
+ def test_run_workflow__missing_required_input(vellum_client):
474
+ """Confirm that we get an error when a required input is not provided to a Subworkflow Deployment Node"""
475
+
476
+ # GIVEN a Subworkflow Deployment Node missing a required input
477
+ class ExampleSubworkflowDeploymentNode(SubworkflowDeploymentNode):
478
+ deployment = "example_subworkflow_deployment"
479
+ subworkflow_inputs = {}
480
+
481
+ # AND the Subworkflow Deployment API call fails due to missing required input
482
+ vellum_client.execute_workflow_stream.side_effect = ApiError(
483
+ status_code=400, body={"detail": "Missing required input for 'my_var_1'"}
484
+ )
485
+
486
+ # WHEN we run the node
487
+ node = ExampleSubworkflowDeploymentNode()
488
+
489
+ # THEN we should get a NodeException for invalid inputs
490
+ with pytest.raises(NodeException) as exc_info:
491
+ list(node.run())
492
+
493
+ # AND the error should indicate the missing required input
494
+ assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
495
+ assert exc_info.value.message == "Missing required input for 'my_var_1'"
@@ -1,9 +1,11 @@
1
- from collections.abc import Callable
2
- from typing import Any, ClassVar, List, Optional
1
+ from collections.abc import Callable, Sequence
2
+ from typing import Any, ClassVar, Dict, List, Optional, cast
3
3
 
4
4
  from pydash import snake_case
5
5
 
6
6
  from vellum import ChatMessage, PromptBlock
7
+ from vellum.client.types.code_execution_package import CodeExecutionPackage
8
+ from vellum.client.types.code_execution_runtime import CodeExecutionRuntime
7
9
  from vellum.workflows.context import execution_context, get_parent_context
8
10
  from vellum.workflows.errors.types import WorkflowErrorCode
9
11
  from vellum.workflows.exceptions import NodeException
@@ -28,14 +30,14 @@ class ToolCallingNode(BaseNode):
28
30
  functions: List[FunctionDefinition] - The functions that can be called
29
31
  function_callables: List[Callable] - The callables that can be called
30
32
  prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values
33
+ function_configs: Optional[Dict[str, Dict[str, Any]]] - Mapping of function names to their configuration
31
34
  """
32
35
 
33
36
  ml_model: ClassVar[str] = "gpt-4o-mini"
34
37
  blocks: ClassVar[List[PromptBlock]] = []
35
38
  functions: ClassVar[List[Callable[..., Any]]] = []
36
39
  prompt_inputs: ClassVar[Optional[EntityInputsInterface]] = None
37
- # TODO: https://linear.app/vellum/issue/APO-342/support-tool-call-max-retries
38
- max_tool_calls: ClassVar[int] = 1
40
+ function_configs: ClassVar[Optional[Dict[str, Dict[str, Any]]]] = None
39
41
 
40
42
  class Outputs(BaseOutputs):
41
43
  """
@@ -102,13 +104,28 @@ class ToolCallingNode(BaseNode):
102
104
  prompt_inputs=self.prompt_inputs,
103
105
  )
104
106
 
105
- self._function_nodes = {
106
- snake_case(function.__name__): create_function_node(
107
+ self._function_nodes = {}
108
+ for function in self.functions:
109
+ function_name = snake_case(function.__name__)
110
+
111
+ # Get configuration for this function
112
+ config = {}
113
+ if self.function_configs and function.__name__ in self.function_configs:
114
+ config = self.function_configs[function.__name__]
115
+
116
+ packages = config.get("packages", None)
117
+ if packages is not None:
118
+ packages = cast(Sequence[CodeExecutionPackage], packages)
119
+
120
+ runtime_raw = config.get("runtime", "PYTHON_3_11_6")
121
+ runtime = cast(CodeExecutionRuntime, runtime_raw)
122
+
123
+ self._function_nodes[function_name] = create_function_node(
107
124
  function=function,
108
125
  tool_router_node=self.tool_router_node,
126
+ packages=packages,
127
+ runtime=runtime,
109
128
  )
110
- for function in self.functions
111
- }
112
129
 
113
130
  graph_set = set()
114
131
 
@@ -1,10 +1,14 @@
1
- from collections.abc import Callable
1
+ from collections.abc import Callable, Sequence
2
+ import inspect
2
3
  import json
4
+ import types
3
5
  from typing import Any, Iterator, List, Optional, Type, cast
4
6
 
5
7
  from pydash import snake_case
6
8
 
7
9
  from vellum import ChatMessage, PromptBlock
10
+ from vellum.client.types.code_execution_package import CodeExecutionPackage
11
+ from vellum.client.types.code_execution_runtime import CodeExecutionRuntime
8
12
  from vellum.client.types.function_call_chat_message_content import FunctionCallChatMessageContent
9
13
  from vellum.client.types.function_call_chat_message_content_value import FunctionCallChatMessageContentValue
10
14
  from vellum.client.types.string_chat_message_content import StringChatMessageContent
@@ -13,10 +17,12 @@ from vellum.workflows.errors.types import WorkflowErrorCode
13
17
  from vellum.workflows.exceptions import NodeException
14
18
  from vellum.workflows.inputs.base import BaseInputs
15
19
  from vellum.workflows.nodes.bases import BaseNode
20
+ from vellum.workflows.nodes.displayable.code_execution_node.node import CodeExecutionNode
16
21
  from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
17
22
  from vellum.workflows.outputs.base import BaseOutput
18
23
  from vellum.workflows.ports.port import Port
19
24
  from vellum.workflows.references.lazy import LazyReference
25
+ from vellum.workflows.state.base import BaseState
20
26
  from vellum.workflows.state.encoder import DefaultStateEncoder
21
27
  from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
22
28
  from vellum.workflows.types.generics import is_workflow_class
@@ -117,26 +123,35 @@ def create_tool_router_node(
117
123
  },
118
124
  ),
119
125
  )
120
-
121
126
  return node
122
127
 
123
128
 
124
- def create_function_node(function: Callable[..., Any], tool_router_node: Type[ToolRouterNode]) -> Type[FunctionNode]:
129
+ def create_function_node(
130
+ function: Callable[..., Any],
131
+ tool_router_node: Type[ToolRouterNode],
132
+ packages: Optional[Sequence[CodeExecutionPackage]] = None,
133
+ runtime: CodeExecutionRuntime = "PYTHON_3_11_6",
134
+ ) -> Type[FunctionNode]:
125
135
  """
126
136
  Create a FunctionNode class for a given function.
127
137
 
128
- This ensures the callable is properly registered and can be called with the expected arguments.
138
+ For workflow functions: BaseNode
139
+ For regular functions: CodeExecutionNode with embedded function
140
+ Args:
141
+ function: The function to create a node for
142
+ tool_router_node: The tool router node class
143
+ packages: Optional list of packages to install for code execution (only used for regular functions)
144
+ runtime: The runtime to use for code execution (default: "PYTHON_3_11_6")
129
145
  """
130
146
 
131
- # Create a class-level wrapper that calls the original function
132
- def execute_function(self) -> BaseNode.Outputs:
133
- outputs = self.state.meta.node_outputs.get(tool_router_node.Outputs.text)
134
- # first parse into json
135
- outputs = json.loads(outputs)
136
- arguments = outputs["arguments"]
147
+ if is_workflow_class(function):
148
+
149
+ def execute_function(self) -> BaseNode.Outputs:
150
+ outputs = self.state.meta.node_outputs.get(tool_router_node.Outputs.text)
151
+ # first parse into json
152
+ outputs = json.loads(outputs)
153
+ arguments = outputs["arguments"]
137
154
 
138
- # Call the function based on its type
139
- if is_workflow_class(function):
140
155
  # Dynamically define an Inputs subclass of BaseInputs
141
156
  Inputs = type(
142
157
  "Inputs",
@@ -160,26 +175,83 @@ def create_function_node(function: Callable[..., Any], tool_router_node: Type[To
160
175
  result = terminal_event.outputs
161
176
  elif terminal_event.name == "workflow.execution.rejected":
162
177
  raise Exception(f"Workflow execution rejected: {terminal_event.error}")
163
- else:
164
- # If it's a regular callable, call it directly
165
- result = function(**arguments)
166
-
167
- self.state.chat_history.append(
168
- ChatMessage(
169
- role="FUNCTION",
170
- content=StringChatMessageContent(value=json.dumps(result, cls=DefaultStateEncoder)),
178
+
179
+ self.state.chat_history.append(
180
+ ChatMessage(
181
+ role="FUNCTION",
182
+ content=StringChatMessageContent(value=json.dumps(result, cls=DefaultStateEncoder)),
183
+ )
171
184
  )
185
+
186
+ return self.Outputs()
187
+
188
+ # Create BaseNode for workflow functions
189
+ node = type(
190
+ f"InlineWorkflowNode_{function.__name__}",
191
+ (FunctionNode,),
192
+ {
193
+ "run": execute_function,
194
+ "__module__": __name__,
195
+ },
172
196
  )
197
+ else:
198
+ # For regular functions, use CodeExecutionNode approach
199
+ function_source = inspect.getsource(function)
200
+ function_name = function.__name__
173
201
 
174
- return self.Outputs()
202
+ code = f'''
203
+ {function_source}
175
204
 
176
- node = type(
177
- f"FunctionNode_{function.__name__}",
178
- (FunctionNode,),
179
- {
180
- "run": execute_function,
181
- "__module__": __name__,
182
- },
183
- )
205
+ def main(arguments):
206
+ """Main function that calls the original function with the provided arguments."""
207
+ return {function_name}(**arguments)
208
+ '''
209
+
210
+ def execute_code_execution_function(self) -> BaseNode.Outputs:
211
+ # Get the function call from the tool router output
212
+ function_call_output = self.state.meta.node_outputs.get(tool_router_node.Outputs.results)
213
+ if function_call_output and len(function_call_output) > 0:
214
+ function_call = function_call_output[0]
215
+ arguments = function_call.value.arguments
216
+ else:
217
+ arguments = {}
218
+
219
+ self.code_inputs = {"arguments": arguments}
220
+
221
+ outputs = base_class.run(self)
222
+
223
+ self.state.chat_history.append(
224
+ ChatMessage(
225
+ role="FUNCTION",
226
+ content=StringChatMessageContent(value=json.dumps(outputs.result, cls=DefaultStateEncoder)),
227
+ )
228
+ )
229
+
230
+ return self.Outputs()
231
+
232
+ # Create the properly typed base class with explicit type annotation
233
+ def get_function_output_type() -> Type:
234
+ return function.__annotations__.get("return", Any)
235
+
236
+ output_type = get_function_output_type()
237
+
238
+ base_class: Type[CodeExecutionNode] = CodeExecutionNode[BaseState, output_type] # type: ignore[valid-type]
239
+
240
+ # Create the class with basic attributes
241
+ node = types.new_class(
242
+ f"CodeExecutionNode_{function.__name__}",
243
+ (base_class,),
244
+ {},
245
+ lambda ns: ns.update(
246
+ {
247
+ "code": code,
248
+ "code_inputs": {}, # No inputs needed since we handle function call extraction in run()
249
+ "run": execute_code_execution_function,
250
+ "runtime": runtime,
251
+ "packages": packages,
252
+ "__module__": __name__,
253
+ }
254
+ ),
255
+ )
184
256
 
185
257
  return node
@@ -82,10 +82,6 @@ class DictWrapper(dict):
82
82
  def __setattr__(self, name, value):
83
83
  self[name] = value
84
84
 
85
- def __iter__(self):
86
- for key in super().keys():
87
- yield self[key]
88
-
89
85
 
90
86
  def clean_for_dict_wrapper(obj):
91
87
  if isinstance(obj, dict):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.62
3
+ Version: 0.14.64
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -20,22 +20,21 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.8
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Typing :: Typed
23
- Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
24
- Requires-Dist: cdktf (>=0.20.5,<0.21.0)
25
- Requires-Dist: click (==8.1.7)
26
- Requires-Dist: docker (==7.1.0)
23
+ Requires-Dist: Jinja2 (>=3.1.0,<4.0.0)
24
+ Requires-Dist: click (>=8.1.0,<9.0.0)
25
+ Requires-Dist: docker (>=7.1.0,<8.0.0)
27
26
  Requires-Dist: httpx (>=0.21.2)
28
- Requires-Dist: openai (>=1.0.0)
27
+ Requires-Dist: openai (>=1.0.0,<2.0.0)
29
28
  Requires-Dist: orderly-set (>=5.2.2,<6.0.0)
30
29
  Requires-Dist: publication (==0.0.3)
31
30
  Requires-Dist: pydantic (>=1.9.2)
32
31
  Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
33
- Requires-Dist: pydash (==7.0.6)
34
- Requires-Dist: python-dotenv (==1.0.1)
35
- Requires-Dist: pytz (==2025.1)
36
- Requires-Dist: pyyaml (==6.0.2)
37
- Requires-Dist: requests (==2.32.3)
38
- Requires-Dist: tomli (==2.0.2)
32
+ Requires-Dist: pydash (>=7.0.0,<8.0.0)
33
+ Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
34
+ Requires-Dist: pytz (>=2022.0,<2026.0)
35
+ Requires-Dist: pyyaml (>=6.0.0,<7.0.0)
36
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
37
+ Requires-Dist: tomli (>=2.0.0,<3.0.0)
39
38
  Requires-Dist: typing_extensions (>=4.0.0)
40
39
  Project-URL: Repository, https://github.com/vellum-ai/vellum-python-sdks
41
40
  Description-Content-Type: text/markdown
@@ -42,7 +42,7 @@ vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=jUDI2FwVaw0
42
42
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=5_5D5PMzBOeUdVtRlANbfEsu7Gv3r37dLvpfjGAqYac,2330
43
43
  vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=-6Ru9W_vfNdLKLStB40qicMx6WvdejPM3PE54Onqk5w,10943
44
44
  vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=fQV5o83BPTwGX6o-ThN4r7BcIhySyqwpW1JGYWpvSJI,5625
45
- vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=CiklGf5_tDbqE1XQm2mnbtoL01_2JYjcnB4FDTpMImQ,3824
45
+ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=2teCYQSX8g-b8aaC_MY4XSC4GRMTJigPFWNTQEkC_gk,3907
46
46
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=yBWeN4T_lOsDVnNOKWRiT7JYKu0IR5Fx2z99iq6QKSA,3273
47
47
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=3E0UqmgVYdtbj4nyq8gKju8EpMsRHMCQ0KLrJhug3XU,1084
48
48
  vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=1NxFddxWCFtMe_je1cutP7qnoASoG94LJqKhRkoQwvw,3535
@@ -59,7 +59,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=TtzUj3
59
59
  vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=h93ysolmbo2viisyhRnXKHPxiDK0I_dSAbYoHFYIoO4,1953
60
60
  vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py,sha256=BUzHJgjdWnPeZxjFjHfDBKnbFjYjnbXPjc-1hne1B2Y,3965
61
61
  vellum_ee/workflows/display/nodes/vellum/tests/test_templating_node.py,sha256=LSk2gx9TpGXbAqKe8dggQW8yJZqj-Cf0EGJFeGGlEcw,3321
62
- vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py,sha256=NewHcTseNE2ogPzEDJ6KEDD9EYLn9RBZIClMwijIs0I,4581
62
+ vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py,sha256=ZsLIGnJ9QKrXjYeDW8LEN8M9FnWRQ9TohHFyB6144HY,7970
63
63
  vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py,sha256=Khjsb53PKpZuyhKoRMgKAL45eGp5hZqXvHmVeQWRw4w,2289
64
64
  vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=3LS1O4DGPWit05oj_ubeW8AlHGnoBxdUMferGQuAiZs,4851
65
65
  vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=z9Omo676RRc7mQjLoL7hjiHhUj0OWVLhrrb97YTN4QA,4086
@@ -90,13 +90,13 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_
90
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=KkYZc_bZuq1lmDcvUz3QxIqJLpQPCZioD1FHUNsMJY8,11211
91
91
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=aZaqRDrkO3ytcmdM2eKJqHSt60MF070NMj6M2vgzOKc,7711
92
92
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=r748dpS13HtwY7t_KQFExFssxRy0xI2d-wxmhiUHRe0,3850
93
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=RPv0bJGwgzBDK_f16VrplPjSZbTisANSH08BGzaHwmk,8341
93
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=Cx3oY6vPVap0xm_mChqfQw4zzR4pqV36o_SyD8g6jPY,8727
94
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=EL5kfakuoEcwD85dGjhMta-J-PpCHRSDoc80SdbBrQk,2769
95
95
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=RmFUDx8dYdfsOE2CGLvdXqNNRtLLpVzXDN8dqZyMcZ8,5822
96
96
  vellum_ee/workflows/display/types.py,sha256=i4T7ElU5b5h-nA1i3scmEhO1BqmNDc4eJDHavATD88w,2821
97
97
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
98
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
99
- vellum_ee/workflows/display/utils/expressions.py,sha256=u-sIH_hDxJdSjq_0BTP3QmbIDvA-OI1GxyPtmNvxxKc,12595
99
+ vellum_ee/workflows/display/utils/expressions.py,sha256=7gWOsow_fkayY7GnByv1cN_XhbI24dJT-VQ5-WP46FU,12894
100
100
  vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3Vd15ijtSN0V97bpW8,1513
101
101
  vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
102
102
  vellum_ee/workflows/display/vellum.py,sha256=o7mq_vk2Yapu9DDKRz5l76h8EmCAypWGQYe6pryrbB8,3576
@@ -134,7 +134,7 @@ vellum/client/README.md,sha256=qmaVIP42MnxAu8jV7u-CsgVFfs3-pHQODrXdZdFxtaw,4749
134
134
  vellum/client/__init__.py,sha256=AYopGv2ZRVn3zsU8_km6KOvEHDbXiTPCVuYVI7bWvdA,120166
135
135
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
136
136
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
137
- vellum/client/core/client_wrapper.py,sha256=6Ws3Z7WyEV9P2j-NicrbyppAaPyLzK4wAJ5mxUvUdJ4,1869
137
+ vellum/client/core/client_wrapper.py,sha256=EBMKy4AU89W_wlhww7wsWGLScDvYus97WUeayU5Ejd0,1869
138
138
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
139
139
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
140
140
  vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
@@ -1496,7 +1496,7 @@ vellum/workflows/emitters/base.py,sha256=D5SADKIvnbgKwIBgYm77jaqvpo1o0rz4MmuX_mu
1496
1496
  vellum/workflows/environment/__init__.py,sha256=wGHslgSEZ7Octe4C-hNtl84EFelNimgmWQoi7px4-uw,71
1497
1497
  vellum/workflows/environment/environment.py,sha256=0XhJPBs8YASWmvPx8bkSdCvcbDmzpe9stfs2kgtNDRU,296
1498
1498
  vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
1499
- vellum/workflows/errors/types.py,sha256=_A0J3ACjg5dAR2TLI1KHSNPI-oDGkt_9nDk5a0NAjTY,3714
1499
+ vellum/workflows/errors/types.py,sha256=nUWuniEfrhdtb-_2GzoDGlYnSJ_yuNUGjVkaKLNr-rM,4049
1500
1500
  vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyuUK48Qw,759
1501
1501
  vellum/workflows/events/node.py,sha256=TaawUxKaZG8cv_GkiKnJmjOmC4Ic0wMlsxUduF2Rbpw,5446
1502
1502
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1548,8 +1548,8 @@ vellum/workflows/inputs/tests/test_inputs.py,sha256=lioA8917mFLYq7Ml69UNkqUjcWbb
1548
1548
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1549
1549
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1550
1550
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1551
- vellum/workflows/nodes/bases/base.py,sha256=WTkFbOynv07TplP5xVvJ5M_IVm1LdosQnT1HKsSwhDM,21942
1552
- vellum/workflows/nodes/bases/base_adornment_node.py,sha256=Ao2opOW4kgNoYXFF9Pk7IMpVZdy6luwrjcqEwU5Q9V0,3404
1551
+ vellum/workflows/nodes/bases/base.py,sha256=TtvfbGnAB8fYfQlhBM-sF3fwZLYn_Nq-dqU9m1fAbBU,21923
1552
+ vellum/workflows/nodes/bases/base_adornment_node.py,sha256=hrgzuTetM4ynPd9YGHoK8Vwwn4XITi3aZZ_OCnQrq4Y,3433
1553
1553
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1554
1554
  vellum/workflows/nodes/bases/tests/test_base_adornment_node.py,sha256=fXZI9KqpS4XMBrBnIEkK3foHaBVvyHwYcQWWDKay7ic,1148
1555
1555
  vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=KRrgjXfzxf1ecE55-jwbQqPnOlNii-i2iaHJUW6oWcI,9358
@@ -1587,21 +1587,21 @@ vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org
1587
1587
  vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=amBXi7Tv50AbGLhfWbwX83PlOdV1XyYRyQmpa6_afE4,3511
1588
1588
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
1589
1589
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=fnjiRWLoRlC4Puo5oQcpZD5Hd-EesxsAo9l5tGAkpZQ,270
1590
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=K2jwAjgG2Qaq7tfDlCckojhAjir962fcIT3eKgjTAEM,11555
1590
+ vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=cD2RzOX9WE6xTKgm09dsEw4xHATZbOjeGyYCSdl8fjU,11785
1591
1591
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1592
1592
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=5CNag1_aEFZbCL0nrOC5e1L-t90-4rp2xDwh0h52hVI,21407
1593
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=T99UWACTD9ytVDVHa6W2go00V7HNwDxOyBFyMM2GnhQ,9567
1593
+ vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=0a40fkkZkFMmZN0CsWf6EP_y1H6x36EGa3WcfVNyOsM,9797
1594
1594
  vellum/workflows/nodes/displayable/bases/search_node.py,sha256=3UtbqY3QO4kzfJHbmUNZGnEEfJmaoiF892u8H6TGjp8,5381
1595
1595
  vellum/workflows/nodes/displayable/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1596
1596
  vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMDPevgwLg1i6YK0g4L4bCy-7xCBN5yYZI,3156
1597
1597
  vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
1598
1598
  vellum/workflows/nodes/displayable/bases/utils.py,sha256=ckMUenSsNkiYmSw6FmjSMHYaCk8Y8_sUjL6lkFFEqts,5412
1599
1599
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1600
- vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=U21jXW8XZoC51vP0pvbbUQzQidR6Ej2lMdGypIUyF3I,9708
1600
+ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=o2hP4tWad3-qXY_QtodtGiOGrO8HjeQnqYrxxpxeJVo,9954
1601
1601
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1602
1602
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1603
1603
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1604
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=mOVOD9UL2X0BpURn3vTI0ZPE7MGyVNo0XIwNKqxxbrM,34723
1604
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=DRd-nOC04RGjXz-ctxADVq7b-hgdiFN3ZfhK6Ld7J8I,36634
1605
1605
  vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=VRTKms59vrSR9mDk99cojParZVAP4lzjEeDwDNXU1tk,3837
1606
1606
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1607
1607
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
@@ -1632,9 +1632,9 @@ vellum/workflows/nodes/displayable/search_node/node.py,sha256=vUTDyurYKw6KLABuVm
1632
1632
  vellum/workflows/nodes/displayable/search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1633
1633
  vellum/workflows/nodes/displayable/search_node/tests/test_node.py,sha256=OserVd6jPe6t49MQF0cxphI2irBLaC_GceMr0acFqoY,8075
1634
1634
  vellum/workflows/nodes/displayable/subworkflow_deployment_node/__init__.py,sha256=9yYM6001YZeqI1VOk1QuEM_yrffk_EdsO7qaPzINKds,92
1635
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py,sha256=biv1H4gIX4B4VMFJ3Rp82NjE65GhmzLq7pREL0ozB2E,9484
1635
+ vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py,sha256=lq8_USZkNiYktH0oJSW2jOuXyRtVwVoq1CKFdCek5-M,9719
1636
1636
  vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1637
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py,sha256=2KdPh1TeIeW_3xJq4QzAwfcuqL6PmMTLNPz4nSaDLmY,18030
1637
+ vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py,sha256=c98nMPogZ6iN_pTvVUMTB3J72Hj--H-XVgvvRXhdSQE,19085
1638
1638
  vellum/workflows/nodes/displayable/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1639
1639
  vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py,sha256=MHuIolSsrY9ziwoXWsye3XOODncL9DLZOkNYzQMLhRw,4696
1640
1640
  vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=VepO5z1277c1y5N6LLIC31nnWD1aak2m5oPFplfJHHs,6935
@@ -1644,9 +1644,9 @@ vellum/workflows/nodes/experimental/__init__.py,sha256=_tpZGWAZLydcKxfrj1-plrZeT
1644
1644
  vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
1645
1645
  vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=cKI2Ls25L-JVt4z4a2ozQa-YBeVy21Z7BQ32Sj7iBPE,10460
1646
1646
  vellum/workflows/nodes/experimental/tool_calling_node/__init__.py,sha256=S7OzT3I4cyOU5Beoz87nPwCejCMP2FsHBFL8OcVmxJ4,118
1647
- vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=4MUlTTAJdFDYA5ybRGaqAQX9XTaOsH48Sr8rJ49oatw,4892
1648
- vellum/workflows/nodes/experimental/tool_calling_node/tests/test_tool_calling_node.py,sha256=sxG26mOwt4N36RLoPJ-ngginPqC5qFzD_kGj9izdCFI,1833
1649
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=Z9zCnvZBE7tT4iwsiG5YgPLFhAKeDJjPs-8JYAi3n8o,7203
1647
+ vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=FkhaJccpCbx2be_IZ5V2v6Lo-jPJ0WgSC5tveLvAW4A,5774
1648
+ vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py,sha256=sxG26mOwt4N36RLoPJ-ngginPqC5qFzD_kGj9izdCFI,1833
1649
+ vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=DjFn33XuomM-EiojCFzTlOsWMdF_uYyHzNayBok6X-k,10055
1650
1650
  vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
1651
1651
  vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1652
1652
  vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
@@ -1687,7 +1687,7 @@ vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
1687
1687
  vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVik6ZVTfVOA,1826
1688
1688
  vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
1689
1689
  vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
1690
- vellum/workflows/types/code_execution_node_wrappers.py,sha256=sPo7fLSyf92n6OFsrNLAsaNs0_I3AH-fXkEw3KrbWmc,3162
1690
+ vellum/workflows/types/code_execution_node_wrappers.py,sha256=3MNIoFZKzVzNS5qFLVuDwMV17QJw72zo7NRf52yMq5A,3074
1691
1691
  vellum/workflows/types/core.py,sha256=kMQremh_I8egXpiKmtMQbB6e3OczAWiRnnTq5V6xlD0,928
1692
1692
  vellum/workflows/types/definition.py,sha256=z81CL_u0FJol-9yUIqoXNTYAARtU8x__c6s-f4rb5c8,2335
1693
1693
  vellum/workflows/types/generics.py,sha256=tKXz0LwWJGKw1YGudyl9_yFDrRgU6yYV1yJV1Zv-LTw,1430
@@ -1712,8 +1712,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1712
1712
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1713
1713
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=fROqff6AZpCIzaSwOKSdtYy4XR0UZQ6ejxL3RJOSJVs,20447
1714
1714
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1715
- vellum_ai-0.14.62.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1716
- vellum_ai-0.14.62.dist-info/METADATA,sha256=Bzub_INWkihzZyUxbedAO9QOhQmT-Ci17-6k_8JW5zE,5484
1717
- vellum_ai-0.14.62.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1718
- vellum_ai-0.14.62.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1719
- vellum_ai-0.14.62.dist-info/RECORD,,
1715
+ vellum_ai-0.14.64.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1716
+ vellum_ai-0.14.64.dist-info/METADATA,sha256=tewaEjPiWmDj6JmUJmAXJPiXr51sX5Yvpc3rHxjJw0w,5508
1717
+ vellum_ai-0.14.64.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1718
+ vellum_ai-0.14.64.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1719
+ vellum_ai-0.14.64.dist-info/RECORD,,
@@ -3,6 +3,7 @@ from typing import Generic, Optional, TypeVar, cast
3
3
 
4
4
  from vellum.workflows.nodes import MapNode
5
5
  from vellum.workflows.types.core import JsonObject
6
+ from vellum.workflows.workflows.base import BaseWorkflow
6
7
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
7
8
  from vellum_ee.workflows.display.nodes.vellum.base_adornment_node import BaseAdornmentNodeDisplay
8
9
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -21,7 +22,7 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
21
22
  node = self._node
22
23
  node_id = self.node_id
23
24
 
24
- subworkflow = raise_if_descriptor(node.subworkflow)
25
+ subworkflow = cast(type[BaseWorkflow], raise_if_descriptor(node.subworkflow))
25
26
 
26
27
  items_node_input = create_node_input(
27
28
  node_id=node_id,
@@ -1,3 +1,4 @@
1
+ from vellum.client.types.code_execution_package import CodeExecutionPackage
1
2
  from vellum.workflows import BaseWorkflow
2
3
  from vellum.workflows.inputs import BaseInputs
3
4
  from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
@@ -122,3 +123,102 @@ def test_serialize_node__prompt_inputs__mixed_values():
122
123
  ],
123
124
  },
124
125
  }
126
+
127
+
128
+ def test_serialize_node__function_configs():
129
+ # GIVEN a tool calling node with function packages
130
+ def foo():
131
+ pass
132
+
133
+ def bar():
134
+ pass
135
+
136
+ class MyToolCallingNode(ToolCallingNode):
137
+ functions = [foo, bar]
138
+ function_configs = {
139
+ "foo": {
140
+ "runtime": "PYTHON_3_11_6",
141
+ "packages": [
142
+ CodeExecutionPackage(name="first_package", version="1.0.0"),
143
+ CodeExecutionPackage(name="second_package", version="2.0.0"),
144
+ ],
145
+ },
146
+ "bar": {
147
+ "runtime": "PYTHON_3_11_6",
148
+ "packages": [CodeExecutionPackage(name="third_package", version="3.0.0")],
149
+ },
150
+ }
151
+
152
+ # AND a workflow with the tool calling node
153
+ class Workflow(BaseWorkflow):
154
+ graph = MyToolCallingNode
155
+
156
+ # WHEN the workflow is serialized
157
+ workflow_display = get_workflow_display(workflow_class=Workflow)
158
+ serialized_workflow: dict = workflow_display.serialize()
159
+
160
+ # THEN the node should properly serialize the function packages
161
+ my_tool_calling_node = next(
162
+ node
163
+ for node in serialized_workflow["workflow_raw_data"]["nodes"]
164
+ if node["id"] == str(MyToolCallingNode.__id__)
165
+ )
166
+
167
+ function_configs_attribute = next(
168
+ attribute for attribute in my_tool_calling_node["attributes"] if attribute["name"] == "function_configs"
169
+ )
170
+
171
+ assert function_configs_attribute == {
172
+ "id": "90cc5fc7-9fb3-450f-be5a-e90e7412a601",
173
+ "name": "function_configs",
174
+ "value": {
175
+ "type": "CONSTANT_VALUE",
176
+ "value": {
177
+ "type": "JSON",
178
+ "value": {
179
+ "foo": {
180
+ "runtime": "PYTHON_3_11_6",
181
+ "packages": [
182
+ {"version": "1.0.0", "name": "first_package"},
183
+ {"version": "2.0.0", "name": "second_package"},
184
+ ],
185
+ },
186
+ "bar": {"runtime": "PYTHON_3_11_6", "packages": [{"version": "3.0.0", "name": "third_package"}]},
187
+ },
188
+ },
189
+ },
190
+ }
191
+
192
+
193
+ def test_serialize_node__function_configs__none():
194
+ # GIVEN a tool calling node with no function configs
195
+ def foo():
196
+ pass
197
+
198
+ class MyToolCallingNode(ToolCallingNode):
199
+ functions = [foo]
200
+
201
+ # AND a workflow with the tool calling node
202
+ class Workflow(BaseWorkflow):
203
+ graph = MyToolCallingNode
204
+
205
+ # WHEN the workflow is serialized
206
+ workflow_display = get_workflow_display(workflow_class=Workflow)
207
+ serialized_workflow: dict = workflow_display.serialize()
208
+
209
+ # THEN the node should properly serialize the functions
210
+ my_tool_calling_node = next(
211
+ node
212
+ for node in serialized_workflow["workflow_raw_data"]["nodes"]
213
+ if node["id"] == str(MyToolCallingNode.__id__)
214
+ )
215
+
216
+ function_configs_attribute = next(
217
+ attribute for attribute in my_tool_calling_node["attributes"] if attribute["name"] == "function_configs"
218
+ )
219
+
220
+ assert function_configs_attribute == {
221
+ "id": "5f63f2aa-72d7-47ad-a552-630753418b7d",
222
+ "name": "function_configs",
223
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
224
+ }
@@ -150,6 +150,22 @@ def test_serialize_workflow():
150
150
  },
151
151
  },
152
152
  },
153
+ {
154
+ "id": "a4e3bc9f-7112-4d2f-94fb-7362a85db27a",
155
+ "name": "function_configs",
156
+ "value": {
157
+ "type": "CONSTANT_VALUE",
158
+ "value": {
159
+ "type": "JSON",
160
+ "value": {
161
+ "get_current_weather": {
162
+ "runtime": "PYTHON_3_11_6",
163
+ "packages": [{"version": "2.26.0", "name": "requests"}],
164
+ }
165
+ },
166
+ },
167
+ },
168
+ },
153
169
  {
154
170
  "id": "0f6dc102-3460-4963-91fa-7ba85d65ef7a",
155
171
  "name": "prompt_inputs",
@@ -167,11 +183,6 @@ def test_serialize_workflow():
167
183
  ],
168
184
  },
169
185
  },
170
- {
171
- "id": "5c041b7d-732c-4773-a93a-32211f2af0b3",
172
- "name": "max_tool_calls",
173
- "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 1.0}},
174
- },
175
186
  ],
176
187
  "outputs": [
177
188
  {"id": "e62bc785-a914-4066-b79e-8c89a5d0ec6c", "name": "text", "type": "STRING", "value": None},
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Any, Dict, cast
1
+ from typing import TYPE_CHECKING, Any, Dict, List, cast
2
2
 
3
3
  from vellum.client.types.logical_operator import LogicalOperator
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
@@ -190,6 +190,16 @@ def _serialize_condition(display_context: "WorkflowDisplayContext", condition: B
190
190
  raise UnsupportedSerializationException(f"Unsupported condition type: {condition.__class__.__name__}")
191
191
 
192
192
 
193
+ def serialize_key(key: Any) -> str:
194
+ """Serialize dictionary keys to strings, handling function objects properly."""
195
+ if callable(key):
196
+ return key.__name__
197
+ elif isinstance(key, str):
198
+ return key
199
+ else:
200
+ return str(key)
201
+
202
+
193
203
  def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> JsonObject:
194
204
  if isinstance(value, ConstantValueReference):
195
205
  return serialize_value(display_context, value._value)
@@ -264,10 +274,10 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
264
274
  }
265
275
 
266
276
  if isinstance(value, dict):
267
- serialized_entries = [
277
+ serialized_entries: List[Dict[str, Any]] = [
268
278
  {
269
279
  "id": str(uuid4_from_hash(f"{key}|{val}")),
270
- "key": key,
280
+ "key": serialize_key(key),
271
281
  "value": serialize_value(display_context, val),
272
282
  }
273
283
  for key, val in value.items()