vellum-ai 1.3.3__py3-none-any.whl → 1.3.4__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 (28) hide show
  1. vellum/client/core/client_wrapper.py +2 -2
  2. vellum/client/types/function_definition.py +5 -0
  3. vellum/client/types/scenario_input_audio_variable_value.py +1 -1
  4. vellum/client/types/scenario_input_document_variable_value.py +1 -1
  5. vellum/client/types/scenario_input_image_variable_value.py +1 -1
  6. vellum/client/types/scenario_input_video_variable_value.py +1 -1
  7. vellum/workflows/events/node.py +1 -1
  8. vellum/workflows/events/tests/test_event.py +1 -1
  9. vellum/workflows/events/workflow.py +1 -1
  10. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +15 -2
  11. vellum/workflows/resolvers/resolver.py +18 -2
  12. vellum/workflows/resolvers/tests/test_resolver.py +121 -0
  13. vellum/workflows/runner/runner.py +17 -17
  14. vellum/workflows/state/encoder.py +0 -37
  15. vellum/workflows/utils/functions.py +35 -0
  16. {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/METADATA +1 -1
  17. {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/RECORD +28 -28
  18. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +18 -2
  19. vellum_ee/workflows/display/tests/test_base_workflow_display.py +52 -2
  20. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +1 -0
  21. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +1 -0
  22. vellum_ee/workflows/display/utils/events.py +1 -0
  23. vellum_ee/workflows/display/utils/expressions.py +44 -0
  24. vellum_ee/workflows/display/utils/tests/test_events.py +11 -1
  25. vellum_ee/workflows/display/workflows/base_workflow_display.py +32 -22
  26. {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/LICENSE +0 -0
  27. {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/WHEEL +0 -0
  28. {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/entry_points.txt +0 -0
@@ -27,10 +27,10 @@ class BaseClientWrapper:
27
27
 
28
28
  def get_headers(self) -> typing.Dict[str, str]:
29
29
  headers: typing.Dict[str, str] = {
30
- "User-Agent": "vellum-ai/1.3.3",
30
+ "User-Agent": "vellum-ai/1.3.4",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.3.3",
33
+ "X-Fern-SDK-Version": "1.3.4",
34
34
  **(self.get_custom_headers() or {}),
35
35
  }
36
36
  if self._api_version is not None:
@@ -30,6 +30,11 @@ class FunctionDefinition(UniversalBaseModel):
30
30
  An OpenAPI specification of parameters that are supported by this function.
31
31
  """
32
32
 
33
+ inputs: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None)
34
+ """
35
+ Optional user defined input mappings for this function.
36
+ """
37
+
33
38
  forced: typing.Optional[bool] = pydantic.Field(default=None)
34
39
  """
35
40
  Set this option to true to force the model to return a function call of this function.
@@ -9,7 +9,7 @@ from .vellum_audio import VellumAudio
9
9
 
10
10
  class ScenarioInputAudioVariableValue(UniversalBaseModel):
11
11
  type: typing.Literal["AUDIO"] = "AUDIO"
12
- value: VellumAudio
12
+ value: typing.Optional[VellumAudio] = None
13
13
  input_variable_id: str
14
14
 
15
15
  if IS_PYDANTIC_V2:
@@ -9,7 +9,7 @@ from .vellum_document import VellumDocument
9
9
 
10
10
  class ScenarioInputDocumentVariableValue(UniversalBaseModel):
11
11
  type: typing.Literal["DOCUMENT"] = "DOCUMENT"
12
- value: VellumDocument
12
+ value: typing.Optional[VellumDocument] = None
13
13
  input_variable_id: str
14
14
 
15
15
  if IS_PYDANTIC_V2:
@@ -9,7 +9,7 @@ from .vellum_image import VellumImage
9
9
 
10
10
  class ScenarioInputImageVariableValue(UniversalBaseModel):
11
11
  type: typing.Literal["IMAGE"] = "IMAGE"
12
- value: VellumImage
12
+ value: typing.Optional[VellumImage] = None
13
13
  input_variable_id: str
14
14
 
15
15
  if IS_PYDANTIC_V2:
@@ -9,7 +9,7 @@ from .vellum_video import VellumVideo
9
9
 
10
10
  class ScenarioInputVideoVariableValue(UniversalBaseModel):
11
11
  type: typing.Literal["VIDEO"] = "VIDEO"
12
- value: VellumVideo
12
+ value: typing.Optional[VellumVideo] = None
13
13
  input_variable_id: str
14
14
 
15
15
  if IS_PYDANTIC_V2:
@@ -141,7 +141,7 @@ class NodeExecutionFulfilledEvent(_BaseNodeEvent, Generic[OutputsType]):
141
141
 
142
142
  class NodeExecutionRejectedBody(_BaseNodeExecutionBody):
143
143
  error: WorkflowError
144
- traceback: Optional[str] = None
144
+ stacktrace: Optional[str] = None
145
145
 
146
146
 
147
147
  class NodeExecutionRejectedEvent(_BaseNodeEvent):
@@ -261,7 +261,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
261
261
  "message": "Workflow failed",
262
262
  "code": "USER_DEFINED_ERROR",
263
263
  },
264
- "traceback": None,
264
+ "stacktrace": None,
265
265
  },
266
266
  "parent": None,
267
267
  "links": None,
@@ -156,7 +156,7 @@ class WorkflowExecutionFulfilledEvent(_BaseWorkflowEvent, Generic[OutputsType]):
156
156
 
157
157
  class WorkflowExecutionRejectedBody(_BaseWorkflowExecutionBody):
158
158
  error: WorkflowError
159
- traceback: Optional[str] = None
159
+ stacktrace: Optional[str] = None
160
160
 
161
161
 
162
162
  class WorkflowExecutionRejectedEvent(_BaseWorkflowEvent):
@@ -520,12 +520,25 @@ def create_function_node(
520
520
  },
521
521
  )
522
522
  else:
523
- # For regular functions, use FunctionNode
523
+
524
+ def create_function_wrapper(func):
525
+ def wrapper(self, **kwargs):
526
+ merged_kwargs = kwargs.copy()
527
+ inputs = getattr(func, "__vellum_inputs__", {})
528
+ if inputs:
529
+ for param_name, param_ref in inputs.items():
530
+ resolved_value = param_ref.resolve(self.state)
531
+ merged_kwargs[param_name] = resolved_value
532
+
533
+ return func(**merged_kwargs)
534
+
535
+ return wrapper
536
+
524
537
  node = type(
525
538
  f"FunctionNode_{function.__name__}",
526
539
  (FunctionNode,),
527
540
  {
528
- "function_definition": lambda self, **kwargs: function(**kwargs), # ← Revert back to lambda
541
+ "function_definition": create_function_wrapper(function),
529
542
  "function_call_output": tool_prompt_node.Outputs.results,
530
543
  "__module__": __name__,
531
544
  },
@@ -1,10 +1,11 @@
1
1
  import logging
2
2
  from uuid import UUID
3
- from typing import Iterator, List, Optional, Tuple, Union
3
+ from typing import Iterator, List, Optional, Tuple, Type, Union
4
4
 
5
5
  from vellum.client.types.vellum_span import VellumSpan
6
6
  from vellum.client.types.workflow_execution_initiated_event import WorkflowExecutionInitiatedEvent
7
7
  from vellum.workflows.events.workflow import WorkflowEvent
8
+ from vellum.workflows.nodes.utils import cast_to_output_type
8
9
  from vellum.workflows.resolvers.base import BaseWorkflowResolver
9
10
  from vellum.workflows.resolvers.types import LoadStateResult
10
11
  from vellum.workflows.state.base import BaseState
@@ -51,6 +52,21 @@ class VellumResolver(BaseWorkflowResolver):
51
52
 
52
53
  return previous_trace_id, root_trace_id, previous_span_id, root_span_id
53
54
 
55
+ def _deserialize_state(self, state_data: dict, state_class: Type[BaseState]) -> BaseState:
56
+ """Deserialize state data with proper type conversion for complex types like List[ChatMessage]."""
57
+ converted_data = {}
58
+
59
+ annotations = getattr(state_class, "__annotations__", {})
60
+
61
+ for field_name, field_value in state_data.items():
62
+ if field_name in annotations:
63
+ field_type = annotations[field_name]
64
+ converted_data[field_name] = cast_to_output_type(field_value, field_type)
65
+ else:
66
+ converted_data[field_name] = field_value
67
+
68
+ return state_class(**converted_data)
69
+
54
70
  def load_state(self, previous_execution_id: Optional[Union[UUID, str]] = None) -> Optional[LoadStateResult]:
55
71
  if isinstance(previous_execution_id, UUID):
56
72
  previous_execution_id = str(previous_execution_id)
@@ -83,7 +99,7 @@ class VellumResolver(BaseWorkflowResolver):
83
99
 
84
100
  if self._workflow_class:
85
101
  state_class = self._workflow_class.get_state_class()
86
- state = state_class(**response.state)
102
+ state = self._deserialize_state(response.state, state_class)
87
103
  else:
88
104
  logger.warning("No workflow class registered, falling back to BaseState")
89
105
  state = BaseState(**response.state)
@@ -1,7 +1,9 @@
1
1
  from datetime import datetime
2
2
  from unittest.mock import Mock
3
3
  from uuid import uuid4
4
+ from typing import List
4
5
 
6
+ from vellum import ChatMessage
5
7
  from vellum.client.types.span_link import SpanLink
6
8
  from vellum.client.types.vellum_code_resource_definition import VellumCodeResourceDefinition
7
9
  from vellum.client.types.workflow_execution_detail import WorkflowExecutionDetail
@@ -129,3 +131,122 @@ def test_load_state_with_context_success():
129
131
  mock_client.workflow_executions.retrieve_workflow_execution_detail.assert_called_once_with(
130
132
  execution_id=str(execution_id)
131
133
  )
134
+
135
+
136
+ def test_load_state_with_chat_message_list():
137
+ """Test load_state successfully loads state with chat_history containing ChatMessage list."""
138
+ resolver = VellumResolver()
139
+ execution_id = uuid4()
140
+ root_execution_id = uuid4()
141
+
142
+ class TestStateWithChatHistory(BaseState):
143
+ test_key: str = "test_value"
144
+ chat_history: List[ChatMessage] = []
145
+
146
+ class TestWorkflow(BaseWorkflow[BaseInputs, TestStateWithChatHistory]):
147
+ pass
148
+
149
+ # GIVEN a state dictionary with chat_history containing ChatMessage objects
150
+ prev_id = str(uuid4())
151
+ prev_span_id = str(uuid4())
152
+ state_dict = {
153
+ "test_key": "test_value",
154
+ "chat_history": [
155
+ {"role": "USER", "text": "Hello, how are you?"},
156
+ {"role": "ASSISTANT", "text": "I'm doing well, thank you!"},
157
+ {"role": "USER", "text": "What can you help me with?"},
158
+ ],
159
+ "meta": {
160
+ "workflow_definition": "MockWorkflow",
161
+ "id": prev_id,
162
+ "span_id": prev_span_id,
163
+ "updated_ts": datetime.now().isoformat(),
164
+ "workflow_inputs": BaseInputs(),
165
+ "external_inputs": {},
166
+ "node_outputs": {},
167
+ "node_execution_cache": NodeExecutionCache(),
168
+ "parent": None,
169
+ },
170
+ }
171
+
172
+ mock_workflow_definition = VellumCodeResourceDefinition(
173
+ name="TestWorkflow", module=["test", "module"], id=str(uuid4())
174
+ )
175
+
176
+ mock_body = WorkflowExecutionInitiatedBody(workflow_definition=mock_workflow_definition, inputs={})
177
+
178
+ previous_trace_id = str(uuid4())
179
+ root_trace_id = str(uuid4())
180
+
181
+ previous_invocation = WorkflowExecutionInitiatedEvent(
182
+ id=str(uuid4()),
183
+ timestamp=datetime.now(),
184
+ trace_id=previous_trace_id,
185
+ span_id=str(execution_id),
186
+ body=mock_body,
187
+ links=[
188
+ SpanLink(
189
+ trace_id=previous_trace_id,
190
+ type="PREVIOUS_SPAN",
191
+ span_context=WorkflowParentContext(workflow_definition=mock_workflow_definition, span_id=str(uuid4())),
192
+ ),
193
+ SpanLink(
194
+ trace_id=root_trace_id,
195
+ type="ROOT_SPAN",
196
+ span_context=WorkflowParentContext(
197
+ workflow_definition=mock_workflow_definition, span_id=str(root_execution_id)
198
+ ),
199
+ ),
200
+ ],
201
+ )
202
+
203
+ root_invocation = WorkflowExecutionInitiatedEvent(
204
+ id=str(uuid4()),
205
+ timestamp=datetime.now(),
206
+ trace_id=root_trace_id,
207
+ span_id=str(root_execution_id),
208
+ body=mock_body,
209
+ links=None,
210
+ )
211
+
212
+ mock_span = WorkflowExecutionSpan(
213
+ span_id=str(execution_id),
214
+ start_ts=datetime.now(),
215
+ end_ts=datetime.now(),
216
+ attributes=WorkflowExecutionSpanAttributes(label="Test Workflow", workflow_id=str(uuid4())),
217
+ events=[previous_invocation, root_invocation],
218
+ )
219
+
220
+ mock_response = WorkflowExecutionDetail(
221
+ span_id="test-span-id", start=datetime.now(), inputs=[], outputs=[], spans=[mock_span], state=state_dict
222
+ )
223
+
224
+ mock_client = Mock()
225
+ mock_client.workflow_executions.retrieve_workflow_execution_detail.return_value = mock_response
226
+
227
+ # AND context with the test workflow class is set up
228
+ context = WorkflowContext(vellum_client=mock_client)
229
+ TestWorkflow(context=context, resolvers=[resolver])
230
+
231
+ # WHEN load_state is called
232
+ result = resolver.load_state(previous_execution_id=execution_id)
233
+
234
+ # THEN should return LoadStateResult with state containing chat_history
235
+ assert isinstance(result, LoadStateResult)
236
+ assert result.state is not None
237
+ assert isinstance(result.state, TestStateWithChatHistory)
238
+ assert result.state.test_key == "test_value"
239
+
240
+ # AND the chat_history should be properly deserialized as ChatMessage objects
241
+ assert len(result.state.chat_history) == 3
242
+ assert all(isinstance(msg, ChatMessage) for msg in result.state.chat_history)
243
+ assert result.state.chat_history[0].role == "USER"
244
+ assert result.state.chat_history[0].text == "Hello, how are you?"
245
+ assert result.state.chat_history[1].role == "ASSISTANT"
246
+ assert result.state.chat_history[1].text == "I'm doing well, thank you!"
247
+ assert result.state.chat_history[2].role == "USER"
248
+ assert result.state.chat_history[2].text == "What can you help me with?"
249
+
250
+ mock_client.workflow_executions.retrieve_workflow_execution_detail.assert_called_once_with(
251
+ execution_id=str(execution_id)
252
+ )
@@ -404,7 +404,7 @@ class WorkflowRunner(Generic[StateType]):
404
404
  )
405
405
  except NodeException as e:
406
406
  logger.info(e)
407
- captured_traceback = traceback.format_exc()
407
+ captured_stacktrace = traceback.format_exc()
408
408
 
409
409
  self._workflow_event_inner_queue.put(
410
410
  NodeExecutionRejectedEvent(
@@ -413,14 +413,14 @@ class WorkflowRunner(Generic[StateType]):
413
413
  body=NodeExecutionRejectedBody(
414
414
  node_definition=node.__class__,
415
415
  error=e.error,
416
- traceback=captured_traceback,
416
+ stacktrace=captured_stacktrace,
417
417
  ),
418
418
  parent=execution.parent_context,
419
419
  )
420
420
  )
421
421
  except WorkflowInitializationException as e:
422
422
  logger.info(e)
423
- captured_traceback = traceback.format_exc()
423
+ captured_stacktrace = traceback.format_exc()
424
424
  self._workflow_event_inner_queue.put(
425
425
  NodeExecutionRejectedEvent(
426
426
  trace_id=execution.trace_id,
@@ -428,7 +428,7 @@ class WorkflowRunner(Generic[StateType]):
428
428
  body=NodeExecutionRejectedBody(
429
429
  node_definition=node.__class__,
430
430
  error=e.error,
431
- traceback=captured_traceback,
431
+ stacktrace=captured_stacktrace,
432
432
  ),
433
433
  parent=execution.parent_context,
434
434
  )
@@ -713,13 +713,13 @@ class WorkflowRunner(Generic[StateType]):
713
713
  )
714
714
 
715
715
  def _reject_workflow_event(
716
- self, error: WorkflowError, captured_traceback: Optional[str] = None
716
+ self, error: WorkflowError, captured_stacktrace: Optional[str] = None
717
717
  ) -> WorkflowExecutionRejectedEvent:
718
- if captured_traceback is None:
718
+ if captured_stacktrace is None:
719
719
  try:
720
- captured_traceback = traceback.format_exc()
721
- if captured_traceback.strip() == "NoneType: None":
722
- captured_traceback = None
720
+ captured_stacktrace = traceback.format_exc()
721
+ if captured_stacktrace.strip() == "NoneType: None":
722
+ captured_stacktrace = None
723
723
  except Exception:
724
724
  pass
725
725
 
@@ -729,7 +729,7 @@ class WorkflowRunner(Generic[StateType]):
729
729
  body=WorkflowExecutionRejectedBody(
730
730
  workflow_definition=self.workflow.__class__,
731
731
  error=error,
732
- traceback=captured_traceback,
732
+ stacktrace=captured_stacktrace,
733
733
  ),
734
734
  parent=self._execution_context.parent_context,
735
735
  )
@@ -773,21 +773,21 @@ class WorkflowRunner(Generic[StateType]):
773
773
  else:
774
774
  self._concurrency_queue.put((self._initial_state, node_cls, None))
775
775
  except NodeException as e:
776
- captured_traceback = traceback.format_exc()
777
- self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error, captured_traceback))
776
+ captured_stacktrace = traceback.format_exc()
777
+ self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error, captured_stacktrace))
778
778
  return
779
779
  except WorkflowInitializationException as e:
780
- captured_traceback = traceback.format_exc()
781
- self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error, captured_traceback))
780
+ captured_stacktrace = traceback.format_exc()
781
+ self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error, captured_stacktrace))
782
782
  return
783
783
  except Exception:
784
784
  err_message = f"An unexpected error occurred while initializing node {node_cls.__name__}"
785
785
  logger.exception(err_message)
786
- captured_traceback = traceback.format_exc()
786
+ captured_stacktrace = traceback.format_exc()
787
787
  self._workflow_event_outer_queue.put(
788
788
  self._reject_workflow_event(
789
789
  WorkflowError(code=WorkflowErrorCode.INTERNAL_ERROR, message=err_message),
790
- captured_traceback,
790
+ captured_stacktrace,
791
791
  )
792
792
  )
793
793
  return
@@ -838,7 +838,7 @@ class WorkflowRunner(Generic[StateType]):
838
838
 
839
839
  if rejection_event:
840
840
  self._workflow_event_outer_queue.put(
841
- self._reject_workflow_event(rejection_event.error, rejection_event.body.traceback)
841
+ self._reject_workflow_event(rejection_event.error, rejection_event.body.stacktrace)
842
842
  )
843
843
  return
844
844
 
@@ -1,11 +1,8 @@
1
1
  from dataclasses import asdict, is_dataclass
2
2
  from datetime import datetime
3
3
  import enum
4
- import inspect
5
- from io import StringIO
6
4
  from json import JSONEncoder
7
5
  from queue import Queue
8
- import sys
9
6
  from uuid import UUID
10
7
  from typing import Any, Callable, Dict, Type
11
8
 
@@ -17,23 +14,6 @@ from vellum.workflows.inputs.base import BaseInputs
17
14
  from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
18
15
  from vellum.workflows.ports.port import Port
19
16
  from vellum.workflows.state.base import BaseState, NodeExecutionCache
20
- from vellum.workflows.utils.functions import compile_function_definition
21
-
22
-
23
- def virtual_open(file_path: str, mode: str = "r"):
24
- """
25
- Open a file, checking VirtualFileFinder instances first before falling back to regular open().
26
- """
27
- for finder in sys.meta_path:
28
- if hasattr(finder, "loader") and hasattr(finder.loader, "_get_code"):
29
- namespace = finder.loader.namespace
30
- if file_path.startswith(namespace + "/"):
31
- relative_path = file_path[len(namespace) + 1 :]
32
- content = finder.loader._get_code(relative_path)
33
- if content is not None:
34
- return StringIO(content)
35
-
36
- return open(file_path, mode)
37
17
 
38
18
 
39
19
  class DefaultStateEncoder(JSONEncoder):
@@ -80,23 +60,6 @@ class DefaultStateEncoder(JSONEncoder):
80
60
  if isinstance(obj, type):
81
61
  return str(obj)
82
62
 
83
- if callable(obj):
84
- function_definition = compile_function_definition(obj)
85
- source_path = inspect.getsourcefile(obj)
86
- if source_path is not None:
87
- with virtual_open(source_path) as f:
88
- source_code = f.read()
89
- else:
90
- source_code = f"# Error: Source code not available for {obj.__name__}"
91
-
92
- return {
93
- "type": "CODE_EXECUTION",
94
- "name": function_definition.name,
95
- "description": function_definition.description,
96
- "definition": function_definition,
97
- "src": source_code,
98
- }
99
-
100
63
  if obj.__class__ in self.encoders:
101
64
  return self.encoders[obj.__class__](obj)
102
65
 
@@ -135,6 +135,9 @@ def _compile_workflow_deployment_input(input_var: Any) -> dict[str, Any]:
135
135
  def compile_function_definition(function: Callable) -> FunctionDefinition:
136
136
  """
137
137
  Converts a Python function into our Vellum-native FunctionDefinition type.
138
+
139
+ Args:
140
+ function: The Python function to compile
138
141
  """
139
142
 
140
143
  try:
@@ -142,10 +145,18 @@ def compile_function_definition(function: Callable) -> FunctionDefinition:
142
145
  except ValueError as e:
143
146
  raise ValueError(f"Failed to get signature for function {function.__name__}: {str(e)}")
144
147
 
148
+ # Get inputs from the decorator if present
149
+ inputs = getattr(function, "__vellum_inputs__", {})
150
+ exclude_params = set(inputs.keys())
151
+
145
152
  properties = {}
146
153
  required = []
147
154
  defs: dict[str, Any] = {}
148
155
  for param in signature.parameters.values():
156
+ # Skip parameters that are in the exclude_params set
157
+ if exclude_params and param.name in exclude_params:
158
+ continue
159
+
149
160
  # Check if parameter uses Annotated type hint
150
161
  if get_origin(param.annotation) is Annotated:
151
162
  args = get_args(param.annotation)
@@ -248,3 +259,27 @@ def compile_workflow_deployment_function_definition(
248
259
  description=description,
249
260
  parameters=parameters,
250
261
  )
262
+
263
+
264
+ def use_tool_inputs(**inputs):
265
+ """
266
+ Decorator to specify which parameters of a tool function should be provided
267
+ from the parent workflow inputs rather than from the LLM.
268
+
269
+ Args:
270
+ **inputs: Mapping of parameter names to parent input references
271
+
272
+ Example:
273
+ @use_tool_inputs(
274
+ parent_input=ParentInputs.parent_input,
275
+ )
276
+ def get_string(parent_input: str, user_query: str) -> str:
277
+ return f"Parent: {parent_input}, Query: {user_query}"
278
+ """
279
+
280
+ def decorator(func: Callable) -> Callable:
281
+ # Store the inputs mapping on the function for later use
282
+ setattr(func, "__vellum_inputs__", inputs)
283
+ return func
284
+
285
+ return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.3.3
3
+ Version: 1.3.4
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -39,7 +39,7 @@ vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqU
39
39
  vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
40
40
  vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=TD23uXA4vhRvsfiq7B10XHpFnsS1CA9M7WH9jsOYNB0,9242
41
41
  vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=FHhPoGmmny4Xcxi2pm12Sk3ZOREanWEVrOWcjRhncH4,6337
42
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=w8tO4EZj4Swy5e_2lIYTeoWzghj8ikMLnjML5OqevLg,4570
42
+ vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=GfwA2Tn0OpdIUiPW9tD2dyv33e3M8yhGt9VfOeN81bU,5146
43
43
  vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=dtO9A-rjbDEJrywwrOxwEXahqrW-S493OIDHOti9Sjs,11498
44
44
  vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=8tSb8qGVoRIELubu0qPeoDlt1LpiIqc6d9_30GWRd_k,2266
45
45
  vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=eeu8i01HbLDvO3KwHnAfaMrYKiEchNAdDdjufjCi6OU,2991
@@ -72,7 +72,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=rHybfUAWwa0L
72
72
  vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=47fOnSCEFnY8th9m2yTYlgnoUuzgyRZdjg-SXwn--lk,4079
73
73
  vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=oICunzyaXPs0tYnW5zH1r93Bx35MSH7mcD-n0DEWRok,4978
74
74
  vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=l0ljTRBpoTJ5oKL-fNcU5qeruLw2pUvQLOJodg0yYLg,12436
75
+ vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=KUxFl04uZv1xPfnLHsMOjPKygwJkf6fRvPj0JvV8Des,14190
76
76
  vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=Y-ajeT65b5varmrZCw6L3hir4hJCFq-eO0jZfRcrs7g,1886
@@ -88,7 +88,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_stat
88
88
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=03Mk8OE3kWcoZW9lNBe7v4KThCYN-pXg5Rjbkfx-jOQ,6031
89
89
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=-T0cd2jx1bC0ZNtAESF78fnYD_0nOqo8zMMLwRHUTRM,6227
90
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=LnZp1YXDn-AaaxiYgxrhCQeH-rLgmlu_r_lvM65EQ5w,7517
91
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=fze2nu8UlH36giAxu60YQzmYwOm8QK3tv_v7MYBm3WY,26306
91
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=9w90pk1DzX2PDRML401bZpD9hdTW53Dw5oGb58mxpiw,26358
92
92
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=yKmOyunzt5_0cUcqhvCmV2pu81TTkshVi_uN3yt76Wc,21816
93
93
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=W4--Ldih7mRMnfyJ9G7kdyeoKkeebSu_5d33TJQzShU,16735
94
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=UnfWTfKN8DrOLpjCfWMgENJBjgNLWcRVfexbwERKY-c,8501
@@ -100,7 +100,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_nod
100
100
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=oVXCjkU0G56QJmqnd_xIwF3D9bhJwALFibM2wmRhwUk,3739
101
101
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=Sg82qV5NCzQDy-RD90hA6QaHgFHOq6ESNkbWXygsnNw,26367
102
102
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py,sha256=QhQbijeCnFeX1i3SMjHJg2WVAEt5JEO3dhFRv-mofdA,2458
103
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=-7NvpYUZEl2T15AS9MPt_scyhKmUmS3wEPa320sgWOI,10085
103
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=cvjHGrgN1LbRvuSKq2U7eLrRFDK4Rb6VJt8L2HIC8HY,10137
104
104
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=XIZZr5POo2NLn2uEWm9EC3rejeBMoO4X-JtzTH6mvp4,4074
105
105
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
106
106
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=exT7U-axwtYgFylagScflSQLJEND51qIAx2UATju6JM,6023
@@ -109,17 +109,17 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_par
109
109
  vellum_ee/workflows/display/types.py,sha256=cyZruu4sXAdHjwuFc7dydM4DcFNf-pp_CmulXItxac4,3679
110
110
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
111
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
112
- vellum_ee/workflows/display/utils/events.py,sha256=KC684ZWODHRGgJtik_KjGXIjM5x4_58_1dGuco_9O8c,1748
112
+ vellum_ee/workflows/display/utils/events.py,sha256=MEG2BT6GgAzkbv1dMaFpov5OShtaAZeAb1-g3xDFsAM,1826
113
113
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
114
- vellum_ee/workflows/display/utils/expressions.py,sha256=yPrUIEQKL5fDwUC9NpVO9yjL6emTOdFJw8VjoZgZ0E0,16942
114
+ vellum_ee/workflows/display/utils/expressions.py,sha256=5DwluPppoJSsMf0pfbRafPIisZHZ4VFueXR1YeLcRwY,18593
115
115
  vellum_ee/workflows/display/utils/registry.py,sha256=1qXiBTdsnro6FeCX0FGBEK7CIf6wa--Jt50iZ_nEp_M,3460
116
116
  vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
117
  vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
118
- vellum_ee/workflows/display/utils/tests/test_events.py,sha256=BHRq2Or_z1ri7EVWFdVbhDxSWRsCVI87VeqqJN8YnIQ,3995
118
+ vellum_ee/workflows/display/utils/tests/test_events.py,sha256=Qze6wEmFJx23_sKQhX-i329apWgMeS9zTptWlRca6Ko,4528
119
119
  vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
120
120
  vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
121
121
  vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
122
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=_s7mYr56vdavU-CR1FtOQKQlcSpepVgJia84w2OEOUk,43042
122
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=o1yKDaBv3dECPcJnEtjDZw8Nt9Y5kjijGc7FQi5IhOQ,43745
123
123
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
124
124
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=pb7BTH-ivRnya1LQU3j-MApWk_m8POpPNOdD0oEK82A,37847
125
125
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -153,7 +153,7 @@ vellum/client/README.md,sha256=b6XKeYBBbhQx0v1sHWfM0gIJeJhUFF-aqL2ig7ADa08,5564
153
153
  vellum/client/__init__.py,sha256=T5Ht_w-Mk_9nzGqdadhQB8V20M0vYj7am06ut0A3P1o,73401
154
154
  vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
155
155
  vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
156
- vellum/client/core/client_wrapper.py,sha256=8my-Ir86AssDOMCGyePyF0hcUkPe6DsVFwW880WD_c8,2840
156
+ vellum/client/core/client_wrapper.py,sha256=7aJQmDvTC5-vD5Qkr7IPOm7wV5Tj_nzD5SlG1cAUg9g,2840
157
157
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
158
158
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
159
159
  vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
@@ -420,7 +420,7 @@ vellum/client/types/function_call_prompt_block.py,sha256=YcuS0ndY-avXWIcG5UE9mNa
420
420
  vellum/client/types/function_call_request.py,sha256=5nPW3gx-l9UN-9puJuDaEZKIkUJUd0glcRLaF0j1x_k,684
421
421
  vellum/client/types/function_call_vellum_value.py,sha256=ITXP69WEE3yELyDylbpmLgIxnh4FWdNI14KkIX8boKA,723
422
422
  vellum/client/types/function_call_vellum_value_request.py,sha256=9Xz4AZDQhtrUCICXdlq0IS9hpFr0bFHE6haf3g3Bn9c,752
423
- vellum/client/types/function_definition.py,sha256=cdDwCBQcW5iXgHF7ZBRsLYEitx7oyDLNEwV2CVSWDZA,1657
423
+ vellum/client/types/function_definition.py,sha256=G4mQtdrJzC3I9984YfiyNmvozppEyWqt66NdZBqrgKc,1840
424
424
  vellum/client/types/generate_options_request.py,sha256=2ZXU_zNraXRqcUwky8GqDAnhR10qb2NmcrIUaBFeF6w,746
425
425
  vellum/client/types/generate_request.py,sha256=R_l2CmLeEF-GVIZHyV93Cbt6BBh2WklTslsyNbI3Vfo,1250
426
426
  vellum/client/types/generate_response.py,sha256=_7F1PqE1aQLIBYqKLgw3UDQx7etR15sZgW-VzHfaaWA,1099
@@ -635,13 +635,13 @@ vellum/client/types/rich_text_child_block.py,sha256=JCC-6FLdxHG--gE-L6lJtRNI0pmn
635
635
  vellum/client/types/rich_text_prompt_block.py,sha256=Te2qnJOiCgqV0gaiui8mVjnATX7mFNJjHqXDX4fd5jA,1000
636
636
  vellum/client/types/sandbox_scenario.py,sha256=gaHbNzufZhibRGoo42Ryo6xZNRo30lLU39oSDS6mqIs,808
637
637
  vellum/client/types/scenario_input.py,sha256=BxKRtG7u9boXbavG0kD-_-pFVk19LPzNFPZyfw64Lfo,967
638
- vellum/client/types/scenario_input_audio_variable_value.py,sha256=n5dF5IxRPsE-x4MiH-2hiiOlZSBFD8WbY8cTzOmKyHs,656
638
+ vellum/client/types/scenario_input_audio_variable_value.py,sha256=YsJ9vgdZtZ2KJkB-zK6xSQpg4LzRahQMdnIQVnqLTUw,680
639
639
  vellum/client/types/scenario_input_chat_history_variable_value.py,sha256=FzXccgtyH9iTyGJJ6XcOM0TmzCMaPKWr1FmGtFCvxsQ,799
640
- vellum/client/types/scenario_input_document_variable_value.py,sha256=yMQtXkCuaQ-A30YJFQzgiFZx5LKk-MBi4zniO_xqxDs,674
641
- vellum/client/types/scenario_input_image_variable_value.py,sha256=0Hjj9e0qTAw8DO5Yob4oR9JN_d3SVTImyjpCOX3pRSc,656
640
+ vellum/client/types/scenario_input_document_variable_value.py,sha256=xg-OOAbs7uegG9_O5fV3WhcUsM8hGXFeVL88-FJovec,698
641
+ vellum/client/types/scenario_input_image_variable_value.py,sha256=o3rwhVnXp3XCOKYSgLRzUXDo7k9WVIvqRCgL1hjyO18,680
642
642
  vellum/client/types/scenario_input_json_variable_value.py,sha256=07_Fj9UOzMGvz1H0j1awmMz0Q6ErliwjgKVU_iUyZb8,716
643
643
  vellum/client/types/scenario_input_string_variable_value.py,sha256=PI_pk-WOXVRBSW6PZKDZW11svxd2r9E6bCpl0zC0MTM,717
644
- vellum/client/types/scenario_input_video_variable_value.py,sha256=IoTqaDZFEgdKUI9ACg-UAUdVFGsl6x76ky_c9GbJQB8,656
644
+ vellum/client/types/scenario_input_video_variable_value.py,sha256=6AxOsLtHNZv-1UyBue6fMs5_vLFCXidx7e_9kP0hHrs,680
645
645
  vellum/client/types/search_filters_request.py,sha256=Moxewgl8KwjIy-VBistrchXQAqfsn5fkqO5R2UzLlI8,1276
646
646
  vellum/client/types/search_node_result.py,sha256=2DbnKCus81-fj56IoGZqIL6Tw6hjf5jH0O2P_AXah8o,713
647
647
  vellum/client/types/search_node_result_data.py,sha256=rXs0R0OG2pjTcE6bMp5lN6_cUusFiVUDwkC3pVo5l6o,810
@@ -1729,14 +1729,14 @@ vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy
1729
1729
  vellum/workflows/errors/types.py,sha256=nUWuniEfrhdtb-_2GzoDGlYnSJ_yuNUGjVkaKLNr-rM,4049
1730
1730
  vellum/workflows/events/__init__.py,sha256=V4mh766fyA70WvHelm9kfVZGrUgEKcJ9tJt8EepfQYU,832
1731
1731
  vellum/workflows/events/context.py,sha256=vCfMIPmz4j9Om36rRWa35A_JU_VccWWS52_mZkkqxak,3345
1732
- vellum/workflows/events/node.py,sha256=2R5Yk86v6DNNfgPiCCQjrQ9JYKc71xKNokW2f2EFUUU,5883
1732
+ vellum/workflows/events/node.py,sha256=yHVd-rX2E3qc2XLnZr0fW6uq4ZCMm34mnY2tzYceyOg,5884
1733
1733
  vellum/workflows/events/relational_threads.py,sha256=zmLrBCBYpdpQV0snKH3HleST-_hWAMy2LIT0xScfzi4,1516
1734
1734
  vellum/workflows/events/stream.py,sha256=xhXJTZirFi0xad5neAQNogrIQ4h47fpnKbVC3vCM5Js,889
1735
1735
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1736
1736
  vellum/workflows/events/tests/test_basic_workflow.py,sha256=Pj6orHsXz37jWC5FARi0Sx2Gjf99Owri2Cvr6Chb79k,1765
1737
- vellum/workflows/events/tests/test_event.py,sha256=6R3RuN_lMJ9ooHQ9-HE1p0j2xL0FtGiRxkQ7PucFbD0,18450
1737
+ vellum/workflows/events/tests/test_event.py,sha256=V26TNmbo2aL4sDvcY3nPxCWgcoS4ejF-7VuesMeD04U,18451
1738
1738
  vellum/workflows/events/types.py,sha256=mVrqAH9Hs9SpXm08Hcxdyap_ImQPwGsxRr56rSNMP34,5043
1739
- vellum/workflows/events/workflow.py,sha256=XW9tpAmDb9BPj2RLYpdbN-SJz_UhwqKBTaMJkZM2ahM,8996
1739
+ vellum/workflows/events/workflow.py,sha256=kLSWFXiDZH0TELWoDjQ_kHVomFnw8MVVUPDGIzgyosw,8997
1740
1740
  vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
1741
1741
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1742
1742
  vellum/workflows/expressions/accessor.py,sha256=3lu1-_-dBfZdJvtX-q66jbmRAZtqIfdsh_3_JNuzg1E,4462
@@ -1897,7 +1897,7 @@ vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47
1897
1897
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=in1fbEz5x1tx3uKv9YXdvOncsHucNL8Ro6Go7lBuuOQ,8962
1898
1898
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=GZoeybB9uM7ai8sBLAtUMHrMVgh-WrJDWrKZci6feDs,11892
1899
1899
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=SIu5GCj4tIE4fz-cAcdULtQfqZIhrcc3Doo6TWLXBws,8804
1900
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=wymqUCytUHiWKJxJDvMc7cLdJ69zfxNrHXAmXkFSUiQ,23189
1900
+ vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=Y2MlODE0lRQ4Fci-G3eYZBpI7CQYe5cIykTLcF4jrbE,23602
1901
1901
  vellum/workflows/nodes/displayable/web_search_node/__init__.py,sha256=8FOnEP-n-U68cvxTlJW9wphIAGHq5aqjzLM-DoSSXnU,61
1902
1902
  vellum/workflows/nodes/displayable/web_search_node/node.py,sha256=NQYux2bOtuBF5E4tn-fXi5y3btURPRrNqMSM9MAZYI4,5091
1903
1903
  vellum/workflows/nodes/displayable/web_search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1932,17 +1932,17 @@ vellum/workflows/references/vellum_secret.py,sha256=Od4d19a5yletWMqNfJR5d_mZQUkV
1932
1932
  vellum/workflows/references/workflow_input.py,sha256=W3rOK1EPd2gYHb04WJwmNm1CUSdvZ9LKrs8RMKxACBs,1751
1933
1933
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1934
1934
  vellum/workflows/resolvers/base.py,sha256=wrQiSC02Bw4-dBwgFjJIHsjpe-4xz4rUJs_1RdErKA0,1164
1935
- vellum/workflows/resolvers/resolver.py,sha256=0Y9ArmN_2Gu62TpgYjFvSzhFMMFy7oGWOJh9zg3jaFk,4085
1936
- vellum/workflows/resolvers/tests/test_resolver.py,sha256=JX6-zjIQShNvt4XYmub6zmY0iUf6z90KZynqvEd33qA,5202
1935
+ vellum/workflows/resolvers/resolver.py,sha256=yK-oY0HDsFJcjlNKAm3vpsPKRIFerIh59FcTwuEN5RY,4839
1936
+ vellum/workflows/resolvers/tests/test_resolver.py,sha256=jXkJBb9SwtoH__bBN-ECohpyD0aTIB9ErEvtFhuTMQM,9750
1937
1937
  vellum/workflows/resolvers/types.py,sha256=Hndhlk69g6EKLh_LYg5ILepW5U_h_BYNllfzhS9k8p4,237
1938
1938
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1939
- vellum/workflows/runner/runner.py,sha256=kuW28ozlBi2thQRhgrKigxXtAcOYoGp0M6lYucYsrMw,40321
1939
+ vellum/workflows/runner/runner.py,sha256=UJPCSddeczBcGu06j63KGM2IbbQ5xISTOwP-tFDptaI,40341
1940
1940
  vellum/workflows/sandbox.py,sha256=jwlFFQjHDwmbVoBah_Q3i8K_BrzOt-F6TXFauiyVyIk,3021
1941
1941
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1942
1942
  vellum/workflows/state/base.py,sha256=m9fCqbZn21GshCVCjJTD1dPZEQjFrsMXqlg7tM9fIwM,24283
1943
1943
  vellum/workflows/state/context.py,sha256=khM30U1iDNts5Xp8LXa_WfpkITNITexrDUUFJ5wZ2W4,8445
1944
1944
  vellum/workflows/state/delta.py,sha256=7h8wR10lRCm15SykaPj-gSEvvsMjCwYLPsOx3nsvBQg,440
1945
- vellum/workflows/state/encoder.py,sha256=HdNlabmBOcFSeY_dgn4LNtQEugyzsw3p4mvn2ChC1Io,3380
1945
+ vellum/workflows/state/encoder.py,sha256=EynuS9aCt9Neb-H6HRCinEVZX5olCzME03W1TSXfpxs,1961
1946
1946
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
1947
1947
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1948
1948
  vellum/workflows/state/tests/test_state.py,sha256=zEVFIY2any41X2BA5Us_qqKpzH5HRqmyrUJ04GTO0pU,7484
@@ -1960,7 +1960,7 @@ vellum/workflows/types/tests/test_definition.py,sha256=rvDYjdJ1rvAv0qHBN7i7s-_WA
1960
1960
  vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
1961
1961
  vellum/workflows/types/utils.py,sha256=mTctHITBybpt4855x32oCKALBEcMNLn-9cCmfEKgJHQ,6498
1962
1962
  vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1963
- vellum/workflows/utils/functions.py,sha256=_mJ7vGxHLIByPbfMpDVKsWVEUGlCof1Bc2aZefF56Ns,8967
1963
+ vellum/workflows/utils/functions.py,sha256=6WRRMb_XbxtvhUKOJq5ZChy0KKvlBaQCBiPhvecXT7I,10029
1964
1964
  vellum/workflows/utils/hmac.py,sha256=JJCczc6pyV6DuE1Oa0QVfYPUN_of3zEYmGFib3OZnrE,1135
1965
1965
  vellum/workflows/utils/names.py,sha256=QLUqfJ1tmSEeUwBKTTiv_Qk3QGbInC2RSmlXfGXc8Wo,380
1966
1966
  vellum/workflows/utils/pydantic_schema.py,sha256=eR_bBtY-T0pttJP-ARwagSdCOnwPUtiT3cegm2lzDTQ,1310
@@ -1979,8 +1979,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1979
1979
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1980
1980
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=ptMntHzVyy8ZuzNgeTuk7hREgKQ5UBdgq8VJFSGaW4Y,20832
1981
1981
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1982
- vellum_ai-1.3.3.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1983
- vellum_ai-1.3.3.dist-info/METADATA,sha256=bNuEdxi3O1ua5f1Q56poP2dIfUnw_dHq58lIVX6pMds,5547
1984
- vellum_ai-1.3.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1985
- vellum_ai-1.3.3.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1986
- vellum_ai-1.3.3.dist-info/RECORD,,
1982
+ vellum_ai-1.3.4.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1983
+ vellum_ai-1.3.4.dist-info/METADATA,sha256=b4vvPio8YVqk9c6RUtdneolPn9VxgV12Es1eAIxG0qM,5547
1984
+ vellum_ai-1.3.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1985
+ vellum_ai-1.3.4.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1986
+ vellum_ai-1.3.4.dist-info/RECORD,,
@@ -1,9 +1,9 @@
1
1
  import inspect
2
+ import os
2
3
  from uuid import UUID
3
4
  from typing import ClassVar, Generic, Optional, TypeVar
4
5
 
5
6
  from vellum.workflows.nodes.displayable.code_execution_node import CodeExecutionNode
6
- from vellum.workflows.nodes.displayable.code_execution_node.utils import read_file_from_path
7
7
  from vellum.workflows.types.core import JsonObject
8
8
  from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
9
9
  from vellum_ee.workflows.display.exceptions import NodeValidationError
@@ -11,10 +11,26 @@ from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
11
11
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
12
12
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
13
13
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
14
+ from vellum_ee.workflows.display.utils.expressions import virtual_open
14
15
 
15
16
  _CodeExecutionNodeType = TypeVar("_CodeExecutionNodeType", bound=CodeExecutionNode)
16
17
 
17
18
 
19
+ def _read_file_from_path_with_virtual_support(node_filepath: str, script_filepath: str) -> Optional[str]:
20
+ """
21
+ Read a file using virtual_open which handles VirtualFileFinder instances.
22
+ """
23
+ node_filepath_dir = os.path.dirname(node_filepath)
24
+ normalized_script_filepath = script_filepath.lstrip("./")
25
+ full_filepath = os.path.join(node_filepath_dir, normalized_script_filepath)
26
+
27
+ try:
28
+ with virtual_open(full_filepath, "r") as file:
29
+ return file.read()
30
+ except (FileNotFoundError, IsADirectoryError):
31
+ return None
32
+
33
+
18
34
  class BaseCodeExecutionNodeDisplay(BaseNodeDisplay[_CodeExecutionNodeType], Generic[_CodeExecutionNodeType]):
19
35
  output_id: ClassVar[Optional[UUID]] = None
20
36
  log_output_id: ClassVar[Optional[UUID]] = None
@@ -37,7 +53,7 @@ class BaseCodeExecutionNodeDisplay(BaseNodeDisplay[_CodeExecutionNodeType], Gene
37
53
  code_value = raw_code
38
54
  elif filepath:
39
55
  node_file_path = inspect.getfile(node)
40
- file_code = read_file_from_path(
56
+ file_code = _read_file_from_path_with_virtual_support(
41
57
  node_filepath=node_file_path,
42
58
  script_filepath=filepath,
43
59
  )
@@ -1,5 +1,5 @@
1
1
  from uuid import UUID
2
- from typing import Dict
2
+ from typing import Any, Dict, List, cast
3
3
 
4
4
  from vellum.workflows.inputs import BaseInputs
5
5
  from vellum.workflows.nodes import BaseNode, InlineSubworkflowNode
@@ -8,7 +8,7 @@ from vellum.workflows.ports.port import Port
8
8
  from vellum.workflows.references.lazy import LazyReference
9
9
  from vellum.workflows.state import BaseState
10
10
  from vellum.workflows.workflows.base import BaseWorkflow
11
- from vellum_ee.workflows.display.base import WorkflowInputsDisplay
11
+ from vellum_ee.workflows.display.base import EdgeDisplay, WorkflowInputsDisplay
12
12
  from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
13
13
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
14
14
 
@@ -379,3 +379,53 @@ def test_global_propagation_deep_nested_subworkflows():
379
379
  inner_global_names = {ref.name for ref in inner_display.display_context.global_workflow_input_displays.keys()}
380
380
 
381
381
  assert inner_global_names == {"middle_param", "inner_param", "root_param"}
382
+
383
+
384
+ def test_serialize_workflow_with_edge_display_data():
385
+ """
386
+ Tests that edges with z_index values serialize display_data correctly.
387
+ """
388
+
389
+ # GIVEN a workflow with connected nodes
390
+ class StartNode(BaseNode):
391
+ class Outputs(BaseNode.Outputs):
392
+ result: str
393
+
394
+ class EndNode(BaseNode):
395
+ class Outputs(BaseNode.Outputs):
396
+ final: str
397
+
398
+ class TestWorkflow(BaseWorkflow):
399
+ graph = StartNode >> EndNode
400
+
401
+ class Outputs(BaseWorkflow.Outputs):
402
+ final_result = EndNode.Outputs.final
403
+
404
+ class TestWorkflowDisplay(BaseWorkflowDisplay[TestWorkflow]):
405
+ edge_displays = {
406
+ (StartNode.Ports.default, EndNode): EdgeDisplay(id=UUID("12345678-1234-5678-1234-567812345678"), z_index=5)
407
+ }
408
+
409
+ # WHEN we serialize the workflow with the custom display
410
+ display = get_workflow_display(
411
+ base_display_class=TestWorkflowDisplay,
412
+ workflow_class=TestWorkflow,
413
+ )
414
+ serialized_workflow = display.serialize()
415
+
416
+ # THEN the edge should include display_data with z_index
417
+ workflow_raw_data = cast(Dict[str, Any], serialized_workflow["workflow_raw_data"])
418
+ edges = cast(List[Dict[str, Any]], workflow_raw_data["edges"])
419
+
420
+ edge_with_display_data = None
421
+ for edge in edges:
422
+ if edge["id"] == "12345678-1234-5678-1234-567812345678":
423
+ edge_with_display_data = edge
424
+ break
425
+
426
+ assert edge_with_display_data is not None, "Edge with custom UUID not found"
427
+ assert edge_with_display_data["display_data"] == {"z_index": 5}
428
+
429
+ assert edge_with_display_data["type"] == "DEFAULT"
430
+ assert "source_node_id" in edge_with_display_data
431
+ assert "target_node_id" in edge_with_display_data
@@ -243,6 +243,7 @@ def test_serialize_workflow():
243
243
  "name": "favorite_noun",
244
244
  "description": "Returns the favorite noun of the user",
245
245
  "parameters": {},
246
+ "inputs": None,
246
247
  "forced": None,
247
248
  "strict": None,
248
249
  }
@@ -152,6 +152,7 @@ def test_serialize_workflow():
152
152
  },
153
153
  "required": ["location", "unit"],
154
154
  },
155
+ "inputs": None,
155
156
  "forced": None,
156
157
  "strict": None,
157
158
  },
@@ -36,6 +36,7 @@ def event_enricher(event: WorkflowExecutionInitiatedEvent) -> WorkflowExecutionI
36
36
  dry_run=True,
37
37
  )
38
38
  register_workflow_display_context(event.span_id, workflow_display.display_context)
39
+ event.body.display_context = workflow_display.get_event_display_context()
39
40
 
40
41
  if event.body.workflow_definition.is_dynamic or _should_mark_workflow_dynamic(event):
41
42
  register_workflow_display_class(workflow_definition, workflow_display.__class__)
@@ -1,4 +1,7 @@
1
1
  from dataclasses import asdict, is_dataclass
2
+ import inspect
3
+ from io import StringIO
4
+ import sys
2
5
  from typing import TYPE_CHECKING, Any, Dict, List, cast
3
6
 
4
7
  from pydantic import BaseModel
@@ -48,13 +51,31 @@ from vellum.workflows.references.workflow_input import WorkflowInputReference
48
51
  from vellum.workflows.types.core import JsonArray, JsonObject
49
52
  from vellum.workflows.types.definition import DeploymentDefinition
50
53
  from vellum.workflows.types.generics import is_workflow_class
54
+ from vellum.workflows.utils.functions import compile_function_definition
51
55
  from vellum.workflows.utils.uuids import uuid4_from_hash
52
56
  from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
57
+ from vellum_ee.workflows.server.virtual_file_loader import VirtualFileLoader
53
58
 
54
59
  if TYPE_CHECKING:
55
60
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
56
61
 
57
62
 
63
+ def virtual_open(file_path: str, mode: str = "r"):
64
+ """
65
+ Open a file, checking VirtualFileFinder instances first before falling back to regular open().
66
+ """
67
+ for finder in sys.meta_path:
68
+ if hasattr(finder, "loader") and isinstance(finder.loader, VirtualFileLoader):
69
+ namespace = finder.loader.namespace
70
+ if file_path.startswith(namespace + "/"):
71
+ relative_path = file_path[len(namespace) + 1 :]
72
+ content = finder.loader._get_code(relative_path)
73
+ if content is not None:
74
+ return StringIO(content)
75
+
76
+ return open(file_path, mode)
77
+
78
+
58
79
  def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperator:
59
80
  if isinstance(descriptor, EqualsExpression):
60
81
  return "="
@@ -399,6 +420,29 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
399
420
  dict_value = value.model_dump()
400
421
  return serialize_value(display_context, dict_value)
401
422
 
423
+ if callable(value):
424
+ function_definition = compile_function_definition(value)
425
+ source_path = inspect.getsourcefile(value)
426
+ if source_path is not None:
427
+ with virtual_open(source_path) as f:
428
+ source_code = f.read()
429
+ else:
430
+ source_code = f"Source code not available for {value.__name__}"
431
+
432
+ return {
433
+ "type": "CONSTANT_VALUE",
434
+ "value": {
435
+ "type": "JSON",
436
+ "value": {
437
+ "type": "CODE_EXECUTION",
438
+ "name": function_definition.name,
439
+ "description": function_definition.description,
440
+ "definition": function_definition.model_dump(),
441
+ "src": source_code,
442
+ },
443
+ },
444
+ }
445
+
402
446
  if not isinstance(value, BaseDescriptor):
403
447
  vellum_value = primitive_to_vellum_value(value)
404
448
  return {
@@ -67,9 +67,14 @@ def test_event_enricher_static_workflow(is_dynamic: bool, expected_config: Optio
67
67
  # WHEN the event_enricher is called with mocked dependencies
68
68
  event_enricher(event)
69
69
 
70
- # AND workflow_version_exec_config is set to the expected config
70
+ # THEN workflow_version_exec_config is set to the expected config
71
71
  assert event.body.workflow_version_exec_config == expected_config
72
72
 
73
+ assert event.body.display_context is not None
74
+ assert hasattr(event.body.display_context, "node_displays")
75
+ assert hasattr(event.body.display_context, "workflow_inputs")
76
+ assert hasattr(event.body.display_context, "workflow_outputs")
77
+
73
78
 
74
79
  def test_event_enricher_marks_subworkflow_deployment_as_dynamic():
75
80
  """Test that event_enricher treats subworkflow deployments as dynamic."""
@@ -109,3 +114,8 @@ def test_event_enricher_marks_subworkflow_deployment_as_dynamic():
109
114
 
110
115
  assert hasattr(enriched_event.body, "workflow_version_exec_config")
111
116
  assert enriched_event.body.workflow_version_exec_config is not None
117
+
118
+ assert enriched_event.body.display_context is not None
119
+ assert hasattr(enriched_event.body.display_context, "node_displays")
120
+ assert hasattr(enriched_event.body.display_context, "workflow_inputs")
121
+ assert hasattr(enriched_event.body.display_context, "workflow_outputs")
@@ -329,16 +329,18 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
329
329
  continue
330
330
 
331
331
  target_node_display = self.display_context.node_displays[unadorned_target_node]
332
- edges.append(
333
- {
334
- "id": str(entrypoint_display.edge_display.id),
335
- "source_node_id": str(entrypoint_node_id),
336
- "source_handle_id": str(entrypoint_node_source_handle_id),
337
- "target_node_id": str(target_node_display.node_id),
338
- "target_handle_id": str(target_node_display.get_trigger_id()),
339
- "type": "DEFAULT",
340
- }
341
- )
332
+ entrypoint_edge_dict: Dict[str, Json] = {
333
+ "id": str(entrypoint_display.edge_display.id),
334
+ "source_node_id": str(entrypoint_node_id),
335
+ "source_handle_id": str(entrypoint_node_source_handle_id),
336
+ "target_node_id": str(target_node_display.node_id),
337
+ "target_handle_id": str(target_node_display.get_trigger_id()),
338
+ "type": "DEFAULT",
339
+ }
340
+ display_data = self._serialize_edge_display_data(entrypoint_display.edge_display)
341
+ if display_data is not None:
342
+ entrypoint_edge_dict["display_data"] = display_data
343
+ edges.append(entrypoint_edge_dict)
342
344
 
343
345
  for (source_node_port, target_node), edge_display in self.display_context.edge_displays.items():
344
346
  unadorned_source_node_port = get_unadorned_port(source_node_port)
@@ -353,18 +355,20 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
353
355
  source_node_port_display = self.display_context.port_displays[unadorned_source_node_port]
354
356
  target_node_display = self.display_context.node_displays[unadorned_target_node]
355
357
 
356
- edges.append(
357
- {
358
- "id": str(edge_display.id),
359
- "source_node_id": str(source_node_port_display.node_id),
360
- "source_handle_id": str(source_node_port_display.id),
361
- "target_node_id": str(target_node_display.node_id),
362
- "target_handle_id": str(
363
- target_node_display.get_target_handle_id_by_source_node_id(source_node_port_display.node_id)
364
- ),
365
- "type": "DEFAULT",
366
- }
367
- )
358
+ regular_edge_dict: Dict[str, Json] = {
359
+ "id": str(edge_display.id),
360
+ "source_node_id": str(source_node_port_display.node_id),
361
+ "source_handle_id": str(source_node_port_display.id),
362
+ "target_node_id": str(target_node_display.node_id),
363
+ "target_handle_id": str(
364
+ target_node_display.get_target_handle_id_by_source_node_id(source_node_port_display.node_id)
365
+ ),
366
+ "type": "DEFAULT",
367
+ }
368
+ display_data = self._serialize_edge_display_data(edge_display)
369
+ if display_data is not None:
370
+ regular_edge_dict["display_data"] = display_data
371
+ edges.append(regular_edge_dict)
368
372
 
369
373
  edges.extend(synthetic_output_edges)
370
374
 
@@ -405,6 +409,12 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
405
409
  "output_variables": output_variables,
406
410
  }
407
411
 
412
+ def _serialize_edge_display_data(self, edge_display: EdgeDisplay) -> Optional[JsonObject]:
413
+ """Serialize edge display data, returning None if no display data is present."""
414
+ if edge_display.z_index is not None:
415
+ return {"z_index": edge_display.z_index}
416
+ return None
417
+
408
418
  def _apply_auto_layout(self, nodes_dict_list: List[Dict[str, Any]], edges: List[Json]) -> None:
409
419
  """Apply auto-layout to nodes that are all positioned at (0,0)."""
410
420
  nodes_for_layout: List[Tuple[str, NodeDisplayData]] = []