vellum-ai 0.14.49__py3-none-any.whl → 0.14.50__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. vellum/__init__.py +6 -2
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +6 -2
  4. vellum/client/types/deployment_read.py +1 -1
  5. vellum/client/types/slim_workflow_execution_read.py +2 -2
  6. vellum/client/types/workflow_event_execution_read.py +2 -2
  7. vellum/client/types/{workflow_execution_usage_calculation_fulfilled_body.py → workflow_execution_usage_calculation_error.py} +5 -6
  8. vellum/client/types/workflow_execution_usage_calculation_error_code_enum.py +7 -0
  9. vellum/client/types/workflow_execution_usage_result.py +24 -0
  10. vellum/types/{workflow_execution_usage_calculation_fulfilled_body.py → workflow_execution_usage_calculation_error.py} +1 -1
  11. vellum/types/workflow_execution_usage_calculation_error_code_enum.py +3 -0
  12. vellum/types/workflow_execution_usage_result.py +3 -0
  13. vellum/workflows/nodes/core/map_node/node.py +74 -87
  14. vellum/workflows/nodes/core/map_node/tests/test_node.py +49 -0
  15. vellum/workflows/state/encoder.py +4 -0
  16. vellum/workflows/workflows/base.py +8 -0
  17. {vellum_ai-0.14.49.dist-info → vellum_ai-0.14.50.dist-info}/METADATA +1 -1
  18. {vellum_ai-0.14.49.dist-info → vellum_ai-0.14.50.dist-info}/RECORD +27 -22
  19. vellum_ee/workflows/display/nodes/base_node_display.py +23 -1
  20. vellum_ee/workflows/display/nodes/get_node_display_class.py +1 -24
  21. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +177 -0
  22. vellum_ee/workflows/display/utils/expressions.py +1 -1
  23. vellum_ee/workflows/display/workflows/base_workflow_display.py +3 -24
  24. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +3 -3
  25. {vellum_ai-0.14.49.dist-info → vellum_ai-0.14.50.dist-info}/LICENSE +0 -0
  26. {vellum_ai-0.14.49.dist-info → vellum_ai-0.14.50.dist-info}/WHEEL +0 -0
  27. {vellum_ai-0.14.49.dist-info → vellum_ai-0.14.50.dist-info}/entry_points.txt +0 -0
vellum/__init__.py CHANGED
@@ -544,7 +544,9 @@ from .types import (
544
544
  WorkflowExecutionSpanAttributes,
545
545
  WorkflowExecutionStreamingBody,
546
546
  WorkflowExecutionStreamingEvent,
547
- WorkflowExecutionUsageCalculationFulfilledBody,
547
+ WorkflowExecutionUsageCalculationError,
548
+ WorkflowExecutionUsageCalculationErrorCodeEnum,
549
+ WorkflowExecutionUsageResult,
548
550
  WorkflowExecutionViewOnlineEvalMetricResult,
549
551
  WorkflowExecutionWorkflowResultEvent,
550
552
  WorkflowExpandMetaRequest,
@@ -1177,7 +1179,9 @@ __all__ = [
1177
1179
  "WorkflowExecutionSpanAttributes",
1178
1180
  "WorkflowExecutionStreamingBody",
1179
1181
  "WorkflowExecutionStreamingEvent",
1180
- "WorkflowExecutionUsageCalculationFulfilledBody",
1182
+ "WorkflowExecutionUsageCalculationError",
1183
+ "WorkflowExecutionUsageCalculationErrorCodeEnum",
1184
+ "WorkflowExecutionUsageResult",
1181
1185
  "WorkflowExecutionViewOnlineEvalMetricResult",
1182
1186
  "WorkflowExecutionWorkflowResultEvent",
1183
1187
  "WorkflowExpandMetaRequest",
@@ -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.49",
21
+ "X-Fern-SDK-Version": "0.14.50",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -568,7 +568,9 @@ from .workflow_execution_span import WorkflowExecutionSpan
568
568
  from .workflow_execution_span_attributes import WorkflowExecutionSpanAttributes
569
569
  from .workflow_execution_streaming_body import WorkflowExecutionStreamingBody
570
570
  from .workflow_execution_streaming_event import WorkflowExecutionStreamingEvent
571
- from .workflow_execution_usage_calculation_fulfilled_body import WorkflowExecutionUsageCalculationFulfilledBody
571
+ from .workflow_execution_usage_calculation_error import WorkflowExecutionUsageCalculationError
572
+ from .workflow_execution_usage_calculation_error_code_enum import WorkflowExecutionUsageCalculationErrorCodeEnum
573
+ from .workflow_execution_usage_result import WorkflowExecutionUsageResult
572
574
  from .workflow_execution_view_online_eval_metric_result import WorkflowExecutionViewOnlineEvalMetricResult
573
575
  from .workflow_execution_workflow_result_event import WorkflowExecutionWorkflowResultEvent
574
576
  from .workflow_expand_meta_request import WorkflowExpandMetaRequest
@@ -1154,7 +1156,9 @@ __all__ = [
1154
1156
  "WorkflowExecutionSpanAttributes",
1155
1157
  "WorkflowExecutionStreamingBody",
1156
1158
  "WorkflowExecutionStreamingEvent",
1157
- "WorkflowExecutionUsageCalculationFulfilledBody",
1159
+ "WorkflowExecutionUsageCalculationError",
1160
+ "WorkflowExecutionUsageCalculationErrorCodeEnum",
1161
+ "WorkflowExecutionUsageResult",
1158
1162
  "WorkflowExecutionViewOnlineEvalMetricResult",
1159
1163
  "WorkflowExecutionWorkflowResultEvent",
1160
1164
  "WorkflowExpandMetaRequest",
@@ -50,7 +50,7 @@ class DeploymentRead(UniversalBaseModel):
50
50
 
51
51
  active_model_version_ids: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
52
52
  """
53
- Deprecated. This now always returns a null value.
53
+ Deprecated. This now always returns an empty array.
54
54
  """
55
55
 
56
56
  last_deployed_history_item_id: str = pydantic.Field()
@@ -15,7 +15,7 @@ from .execution_vellum_value import ExecutionVellumValue
15
15
  from .workflow_error import WorkflowError
16
16
  from .workflow_execution_actual import WorkflowExecutionActual
17
17
  from .workflow_execution_view_online_eval_metric_result import WorkflowExecutionViewOnlineEvalMetricResult
18
- from .workflow_execution_usage_calculation_fulfilled_body import WorkflowExecutionUsageCalculationFulfilledBody
18
+ from .workflow_execution_usage_result import WorkflowExecutionUsageResult
19
19
  from ..core.pydantic_utilities import IS_PYDANTIC_V2
20
20
  import pydantic
21
21
 
@@ -30,7 +30,7 @@ class SlimWorkflowExecutionRead(UniversalBaseModel):
30
30
  error: typing.Optional[WorkflowError] = None
31
31
  latest_actual: typing.Optional[WorkflowExecutionActual] = None
32
32
  metric_results: typing.List[WorkflowExecutionViewOnlineEvalMetricResult]
33
- usage_results: typing.List[WorkflowExecutionUsageCalculationFulfilledBody]
33
+ usage_results: typing.Optional[typing.List[WorkflowExecutionUsageResult]] = None
34
34
 
35
35
  if IS_PYDANTIC_V2:
36
36
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -15,7 +15,7 @@ from .execution_vellum_value import ExecutionVellumValue
15
15
  from .workflow_error import WorkflowError
16
16
  from .workflow_execution_actual import WorkflowExecutionActual
17
17
  from .workflow_execution_view_online_eval_metric_result import WorkflowExecutionViewOnlineEvalMetricResult
18
- from .workflow_execution_usage_calculation_fulfilled_body import WorkflowExecutionUsageCalculationFulfilledBody
18
+ from .workflow_execution_usage_result import WorkflowExecutionUsageResult
19
19
  from .vellum_span import VellumSpan
20
20
  from ..core.pydantic_utilities import IS_PYDANTIC_V2
21
21
  import pydantic
@@ -31,7 +31,7 @@ class WorkflowEventExecutionRead(UniversalBaseModel):
31
31
  error: typing.Optional[WorkflowError] = None
32
32
  latest_actual: typing.Optional[WorkflowExecutionActual] = None
33
33
  metric_results: typing.List[WorkflowExecutionViewOnlineEvalMetricResult]
34
- usage_results: typing.List[WorkflowExecutionUsageCalculationFulfilledBody]
34
+ usage_results: typing.Optional[typing.List[WorkflowExecutionUsageResult]] = None
35
35
  spans: typing.List[VellumSpan]
36
36
 
37
37
  if IS_PYDANTIC_V2:
@@ -1,16 +1,15 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
3
  from ..core.pydantic_utilities import UniversalBaseModel
4
- import typing
5
- from .ml_model_usage_wrapper import MlModelUsageWrapper
6
- from .price import Price
4
+ from .workflow_execution_usage_calculation_error_code_enum import WorkflowExecutionUsageCalculationErrorCodeEnum
7
5
  from ..core.pydantic_utilities import IS_PYDANTIC_V2
6
+ import typing
8
7
  import pydantic
9
8
 
10
9
 
11
- class WorkflowExecutionUsageCalculationFulfilledBody(UniversalBaseModel):
12
- usage: typing.List[MlModelUsageWrapper]
13
- cost: typing.List[Price]
10
+ class WorkflowExecutionUsageCalculationError(UniversalBaseModel):
11
+ code: WorkflowExecutionUsageCalculationErrorCodeEnum
12
+ message: str
14
13
 
15
14
  if IS_PYDANTIC_V2:
16
15
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -0,0 +1,7 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ WorkflowExecutionUsageCalculationErrorCodeEnum = typing.Union[
6
+ typing.Literal["UNKNOWN", "DEPENDENCIES_FAILED", "NO_USAGE_CALCULATED", "INTERNAL_SERVER_ERROR"], typing.Any
7
+ ]
@@ -0,0 +1,24 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ..core.pydantic_utilities import UniversalBaseModel
4
+ import typing
5
+ from .ml_model_usage_wrapper import MlModelUsageWrapper
6
+ from .price import Price
7
+ from .workflow_execution_usage_calculation_error import WorkflowExecutionUsageCalculationError
8
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
9
+ import pydantic
10
+
11
+
12
+ class WorkflowExecutionUsageResult(UniversalBaseModel):
13
+ usage: typing.Optional[typing.List[MlModelUsageWrapper]] = None
14
+ cost: typing.Optional[typing.List[Price]] = None
15
+ error: typing.Optional[WorkflowExecutionUsageCalculationError] = None
16
+
17
+ if IS_PYDANTIC_V2:
18
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
19
+ else:
20
+
21
+ class Config:
22
+ frozen = True
23
+ smart_union = True
24
+ extra = pydantic.Extra.allow
@@ -1,3 +1,3 @@
1
1
  # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
2
 
3
- from vellum.client.types.workflow_execution_usage_calculation_fulfilled_body import *
3
+ from vellum.client.types.workflow_execution_usage_calculation_error import *
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.workflow_execution_usage_calculation_error_code_enum import *
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.workflow_execution_usage_result import *
@@ -1,7 +1,7 @@
1
1
  from collections import defaultdict
2
+ import concurrent.futures
2
3
  import logging
3
4
  from queue import Empty, Queue
4
- from threading import Thread
5
5
  from typing import (
6
6
  TYPE_CHECKING,
7
7
  Callable,
@@ -75,95 +75,90 @@ class MapNode(BaseAdornmentNode[StateType], Generic[StateType, MapNodeItemType])
75
75
  return
76
76
 
77
77
  self._event_queue: Queue[Tuple[int, WorkflowEvent]] = Queue()
78
- self._concurrency_queue: Queue[Thread] = Queue()
79
- fulfilled_iterations: List[bool] = []
80
- for index, item in enumerate(self.items):
81
- fulfilled_iterations.append(False)
78
+ fulfilled_iterations: List[bool] = [False] * len(self.items)
79
+
80
+ max_workers = self.max_concurrency if self.max_concurrency is not None else len(self.items)
81
+
82
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
83
+ futures = []
82
84
  current_execution_context = get_execution_context()
83
- thread = Thread(
84
- target=self._context_run_subworkflow,
85
- kwargs={
86
- "item": item,
87
- "index": index,
88
- "current_execution_context": current_execution_context,
89
- },
90
- )
91
- if self.max_concurrency is None:
92
- thread.start()
93
- else:
94
- self._concurrency_queue.put(thread)
95
-
96
- if self.max_concurrency is not None:
97
- concurrency_count = 0
98
- while concurrency_count < self.max_concurrency:
99
- is_empty = self._start_thread()
100
- if is_empty:
101
- break
102
-
103
- concurrency_count += 1
104
-
105
- try:
106
- while map_node_event := self._event_queue.get():
107
- index = map_node_event[0]
108
- subworkflow_event = map_node_event[1]
109
- self._context._emit_subworkflow_event(subworkflow_event)
110
-
111
- if not is_workflow_event(subworkflow_event):
112
- continue
113
-
114
- if subworkflow_event.workflow_definition != self.subworkflow:
115
- continue
116
-
117
- if subworkflow_event.name == "workflow.execution.initiated":
118
- for output_name in mapped_items.keys():
119
- yield BaseOutput(name=output_name, delta=(None, index, "INITIATED"))
120
-
121
- elif subworkflow_event.name == "workflow.execution.fulfilled":
122
- for output_reference, output_value in subworkflow_event.outputs:
123
- if not isinstance(output_reference, OutputReference):
124
- logger.error(
125
- "Invalid key to map node's subworkflow event outputs",
126
- extra={"output_reference_type": type(output_reference)},
85
+ for index, item in enumerate(self.items):
86
+ future = executor.submit(
87
+ self._context_run_subworkflow,
88
+ item=item,
89
+ index=index,
90
+ current_execution_context=current_execution_context,
91
+ )
92
+ futures.append(future)
93
+
94
+ while not all(fulfilled_iterations):
95
+ try:
96
+ map_node_event = self._event_queue.get(block=False)
97
+ index = map_node_event[0]
98
+ subworkflow_event = map_node_event[1]
99
+ self._context._emit_subworkflow_event(subworkflow_event)
100
+
101
+ if not is_workflow_event(subworkflow_event):
102
+ continue
103
+
104
+ if subworkflow_event.workflow_definition != self.subworkflow:
105
+ continue
106
+
107
+ if subworkflow_event.name == "workflow.execution.initiated":
108
+ for output_name in mapped_items.keys():
109
+ yield BaseOutput(name=output_name, delta=(None, index, "INITIATED"))
110
+
111
+ elif subworkflow_event.name == "workflow.execution.fulfilled":
112
+ for output_reference, output_value in subworkflow_event.outputs:
113
+ if not isinstance(output_reference, OutputReference):
114
+ logger.error(
115
+ "Invalid key to map node's subworkflow event outputs",
116
+ extra={"output_reference_type": type(output_reference)},
117
+ )
118
+ continue
119
+
120
+ output_mapped_items = mapped_items[output_reference.name]
121
+ if index < 0 or index >= len(output_mapped_items):
122
+ logger.error(
123
+ "Invalid map node index",
124
+ extra={"index": index, "output_name": output_reference.name},
125
+ )
126
+ continue
127
+
128
+ output_mapped_items[index] = output_value
129
+ yield BaseOutput(
130
+ name=output_reference.name,
131
+ delta=(output_value, index, "FULFILLED"),
127
132
  )
128
- continue
129
133
 
130
- output_mapped_items = mapped_items[output_reference.name]
131
- if index < 0 or index >= len(output_mapped_items):
132
- logger.error(
133
- "Invalid map node index", extra={"index": index, "output_name": output_reference.name}
134
- )
135
- continue
134
+ fulfilled_iterations[index] = True
136
135
 
137
- output_mapped_items[index] = output_value
138
- yield BaseOutput(
139
- name=output_reference.name,
140
- delta=(output_value, index, "FULFILLED"),
136
+ elif subworkflow_event.name == "workflow.execution.paused":
137
+ raise NodeException(
138
+ code=WorkflowErrorCode.INVALID_OUTPUTS,
139
+ message=f"Subworkflow unexpectedly paused on iteration {index}",
141
140
  )
141
+ elif subworkflow_event.name == "workflow.execution.rejected":
142
+ raise NodeException(
143
+ f"Subworkflow failed on iteration {index} with error: {subworkflow_event.error.message}",
144
+ code=subworkflow_event.error.code,
145
+ )
146
+ except Empty:
147
+ all_futures_done = all(future.done() for future in futures)
142
148
 
143
- fulfilled_iterations[index] = True
144
- if all(fulfilled_iterations):
145
- break
146
-
147
- if self.max_concurrency is not None:
148
- self._start_thread()
149
- elif subworkflow_event.name == "workflow.execution.paused":
150
- raise NodeException(
151
- code=WorkflowErrorCode.INVALID_OUTPUTS,
152
- message=f"Subworkflow unexpectedly paused on iteration {index}",
153
- )
154
- elif subworkflow_event.name == "workflow.execution.rejected":
155
- raise NodeException(
156
- f"Subworkflow failed on iteration {index} with error: {subworkflow_event.error.message}",
157
- code=subworkflow_event.error.code,
158
- )
159
- except Empty:
160
- pass
149
+ if all_futures_done:
150
+ if not all(fulfilled_iterations):
151
+ if self._event_queue.empty():
152
+ logger.warning("All threads completed but not all iterations fulfilled")
153
+ break
154
+ else:
155
+ break
161
156
 
162
157
  for output_name, output_list in mapped_items.items():
163
158
  yield BaseOutput(name=output_name, value=output_list)
164
159
 
165
160
  def _context_run_subworkflow(
166
- self, *, item: MapNodeItemType, index: int, current_execution_context: ExecutionContext
161
+ self, item: MapNodeItemType, index: int, current_execution_context: ExecutionContext
167
162
  ) -> None:
168
163
  parent_context = current_execution_context.parent_context
169
164
  trace_id = current_execution_context.trace_id
@@ -186,14 +181,6 @@ class MapNode(BaseAdornmentNode[StateType], Generic[StateType, MapNodeItemType])
186
181
  for event in events:
187
182
  self._event_queue.put((index, event))
188
183
 
189
- def _start_thread(self) -> bool:
190
- if self._concurrency_queue.empty():
191
- return False
192
-
193
- thread = self._concurrency_queue.get()
194
- thread.start()
195
- return True
196
-
197
184
  @overload
198
185
  @classmethod
199
186
  def wrap(
@@ -1,3 +1,5 @@
1
+ import datetime
2
+ import threading
1
3
  import time
2
4
 
3
5
  from vellum.workflows.inputs.base import BaseInputs
@@ -172,3 +174,50 @@ def test_map_node__nested_map_node():
172
174
  ["apple carrot", "apple potato"],
173
175
  ["banana carrot", "banana potato"],
174
176
  ]
177
+
178
+
179
+ def test_map_node_parallel_execution_with_workflow():
180
+ # TODO: Find a better way to test this such that it represents what a user would see.
181
+ # https://linear.app/vellum/issue/APO-482/find-a-better-way-to-test-concurrency-with-map-nodes
182
+ thread_ids = {}
183
+
184
+ # GIVEN a series of nodes that simulate work
185
+ class BaseNode1(BaseNode):
186
+ item = MapNode.SubworkflowInputs.item
187
+
188
+ class Outputs(BaseOutputs):
189
+ output: str
190
+ thread_id: int
191
+
192
+ def run(self) -> Outputs:
193
+ current_thread_id = threading.get_ident()
194
+ thread_ids[self.item] = current_thread_id
195
+
196
+ # Simulate work
197
+ time.sleep(0.01)
198
+
199
+ end = time.time()
200
+ end_str = datetime.datetime.fromtimestamp(end).strftime("%Y-%m-%d %H:%M:%S.%f")
201
+
202
+ return self.Outputs(output=end_str, thread_id=current_thread_id)
203
+
204
+ # AND a workflow that connects these nodes
205
+ class TestWorkflow(BaseWorkflow[MapNode.SubworkflowInputs, BaseState]):
206
+ graph = BaseNode1
207
+
208
+ class Outputs(BaseWorkflow.Outputs):
209
+ final_output = BaseNode1.Outputs.output
210
+ thread_id = BaseNode1.Outputs.thread_id
211
+
212
+ # AND a map node that uses this workflow
213
+ class TestMapNode(MapNode):
214
+ items = [1, 2, 3]
215
+ subworkflow = TestWorkflow
216
+
217
+ # WHEN we run the map node
218
+ node = TestMapNode()
219
+ list(node.run())
220
+
221
+ # AND each item should have run on a different thread
222
+ thread_ids_list = list(thread_ids.values())
223
+ assert len(set(thread_ids_list)) == 3
@@ -13,6 +13,7 @@ from vellum.workflows.inputs.base import BaseInputs
13
13
  from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
14
14
  from vellum.workflows.ports.port import Port
15
15
  from vellum.workflows.state.base import BaseState, NodeExecutionCache
16
+ from vellum.workflows.utils.functions import compile_function_definition
16
17
 
17
18
 
18
19
  class DefaultStateEncoder(JSONEncoder):
@@ -57,6 +58,9 @@ class DefaultStateEncoder(JSONEncoder):
57
58
  if isinstance(obj, type):
58
59
  return str(obj)
59
60
 
61
+ if callable(obj):
62
+ return compile_function_definition(obj)
63
+
60
64
  if obj.__class__ in self.encoders:
61
65
  return self.encoders[obj.__class__](obj)
62
66
 
@@ -276,6 +276,14 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
276
276
  """
277
277
  return cls._get_edges_from_subgraphs(cls.get_unused_subgraphs())
278
278
 
279
+ @classmethod
280
+ def get_all_nodes(cls) -> Iterator[Type[BaseNode]]:
281
+ """
282
+ Returns an iterator over all nodes in the Workflow, used or unused.
283
+ """
284
+ yield from cls.get_nodes()
285
+ yield from cls.get_unused_nodes()
286
+
279
287
  @classmethod
280
288
  def get_entrypoints(cls) -> Iterable[Type[BaseNode]]:
281
289
  return iter({e for g in cls.get_subgraphs() for e in g.entrypoints})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.49
3
+ Version: 0.14.50
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -26,8 +26,8 @@ vellum_ee/workflows/display/base.py,sha256=EqlQFD56kpqMY02ZBJBQajzJKh33Dwi60Wo77
26
26
  vellum_ee/workflows/display/editor/__init__.py,sha256=MSAgY91xCEg2neH5d8jXx5wRdR962ftZVa6vO9BGq9k,167
27
27
  vellum_ee/workflows/display/editor/types.py,sha256=x-tOOCJ6CF4HmiKDfCmcc3bOVfc1EBlP5o6u5WEfLoY,567
28
28
  vellum_ee/workflows/display/nodes/__init__.py,sha256=jI1aPBQf8DkmrYoZ4O-wR1duqZByOf5mDFmo_wFJPE4,307
29
- vellum_ee/workflows/display/nodes/base_node_display.py,sha256=1VoNyAw9MzgGePI2wrOShsNccrbS4bTuu3bnTii0Wu4,15480
30
- vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=gKAZfc7JBLzcwYPchnpHy2nMVMPmltAszOwLyXDrro0,2085
29
+ vellum_ee/workflows/display/nodes/base_node_display.py,sha256=mzODbbNfrjOi7rVQb6FFCEjQHZkTs76nAc8L-Q5yCnQ,16491
30
+ vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=jI_kUi9LnNLDpY63QtlC4TfN8P571VN4LpzH0I1ZtLk,1149
31
31
  vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=Z4Mf7xLCNiblSbpKI0BrV5modQr-ZcFzhfir_OSyTTs,2997
33
33
  vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
@@ -89,19 +89,20 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_
89
89
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=KkYZc_bZuq1lmDcvUz3QxIqJLpQPCZioD1FHUNsMJY8,11211
90
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=aZaqRDrkO3ytcmdM2eKJqHSt60MF070NMj6M2vgzOKc,7711
91
91
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=r748dpS13HtwY7t_KQFExFssxRy0xI2d-wxmhiUHRe0,3850
92
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=rp9suYcwVZPpIQ3ChlWWvahMlaUd7u-31VmesR0Mn8w,7683
92
93
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=EL5kfakuoEcwD85dGjhMta-J-PpCHRSDoc80SdbBrQk,2769
93
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=RmFUDx8dYdfsOE2CGLvdXqNNRtLLpVzXDN8dqZyMcZ8,5822
94
95
  vellum_ee/workflows/display/types.py,sha256=i4T7ElU5b5h-nA1i3scmEhO1BqmNDc4eJDHavATD88w,2821
95
96
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
97
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
97
- vellum_ee/workflows/display/utils/expressions.py,sha256=gbc_UDogJEI1juSNPfR3a-OXu2sIH7w3vLLw4YjOqDc,12416
98
+ vellum_ee/workflows/display/utils/expressions.py,sha256=qsKRgxm9zKFgAgjc9LqKEWP1rtdzXA1NDsXu9kyhf60,12416
98
99
  vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3Vd15ijtSN0V97bpW8,1513
99
100
  vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
100
101
  vellum_ee/workflows/display/vellum.py,sha256=o7mq_vk2Yapu9DDKRz5l76h8EmCAypWGQYe6pryrbB8,3576
101
102
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
102
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=NuWlnGNe3Htcfh-l_8e37uitdUsy6WZNB7W7dYcCoUg,33355
103
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=DbjLChDtlNAs86sWL5-ojYYzwFsOjACAGfquxM3VYcw,32563
103
104
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
104
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=gTKddCTGzjWHJ8v4E0XqCLgxKuSXaFveoN2IT1LxrPw,29472
105
+ vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=rRwXLgsXqiaSn3jzP7lc--pytRW3Jmnj2-zNq5l-FQ4,29472
105
106
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=aaKdmWrgEe5YyV4zuDY_4E3y-l59rIHQnNGiPj2OWxQ,359
106
107
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
108
  vellum_ee/workflows/server/virtual_file_loader.py,sha256=7JphJcSO3H85qiC2DpFfBWjC3JjrbRmoynBC6KKHVsA,2710
@@ -127,12 +128,12 @@ vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIP
127
128
  vellum_ee/workflows/tests/test_display_meta.py,sha256=C25dErwghPNXio49pvSRxyOuc96srH6eYEwTAWdE2zY,2258
128
129
  vellum_ee/workflows/tests/test_server.py,sha256=SsOkS6sGO7uGC4mxvk4iv8AtcXs058P9hgFHzTWmpII,14519
129
130
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
130
- vellum/__init__.py,sha256=4UwLVTlKBQ0cTIJ3NTqxw51aEFNUPfmJ-rVYP1yVUU0,41758
131
+ vellum/__init__.py,sha256=Hqfl49WZJzzqOKzVsTGi-j9twIqFOoRmACJsrEsjL44,41918
131
132
  vellum/client/README.md,sha256=qmaVIP42MnxAu8jV7u-CsgVFfs3-pHQODrXdZdFxtaw,4749
132
133
  vellum/client/__init__.py,sha256=PEnFl7LbXQcvAi3bVN2qyt5xm2FtVtq7xWKkcWM3Tg4,120166
133
134
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
134
135
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
135
- vellum/client/core/client_wrapper.py,sha256=fgLMgzbjkk4zz3CHZ_Wdqz6Sz1JL4L0v7uiNheKIcZA,1869
136
+ vellum/client/core/client_wrapper.py,sha256=0Uo8ifbV1rC2jIjuU95Td5DeLVDs0xqQP3DETAbhbEU,1869
136
137
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
137
138
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
138
139
  vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
@@ -200,7 +201,7 @@ vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCat
200
201
  vellum/client/resources/workspace_secrets/client.py,sha256=zlBdbeTP6sqvtyl_DlrpfG-W5hSP7tJ1NYLSygi4CLU,8205
201
202
  vellum/client/resources/workspaces/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
202
203
  vellum/client/resources/workspaces/client.py,sha256=RthwzN1o-Jxwg5yyNNodavFyNUSxfLoTv26w3mRR5g8,3595
203
- vellum/client/types/__init__.py,sha256=DODWRvaxjqdVCvpr4b6kxku_2WZKSvl7lEl6tSr_gXU,63222
204
+ vellum/client/types/__init__.py,sha256=wFQ3c_Pu8mGQFHIUy5dORq6Afmp5jdl9pnfUizIXjtI,63474
204
205
  vellum/client/types/ad_hoc_execute_prompt_event.py,sha256=bCjujA2XsOgyF3bRZbcEqV2rOIymRgsLoIRtZpB14xg,607
205
206
  vellum/client/types/ad_hoc_expand_meta.py,sha256=1gv-NCsy_6xBYupLvZH979yf2VMdxAU-l0y0ynMKZaw,1331
206
207
  vellum/client/types/ad_hoc_fulfilled_prompt_execution_meta.py,sha256=oDG60TpwK1YNSKhRsBbiP2O3ZF9PKR-M9chGIfKw4R4,1004
@@ -271,7 +272,7 @@ vellum/client/types/create_test_suite_test_case_request.py,sha256=3LmAy6U8tUJ75d
271
272
  vellum/client/types/deployment_history_item.py,sha256=-u7UYEFpVOlFijErPLGO99WYQxAnctZK52W-GM4KJJU,1132
272
273
  vellum/client/types/deployment_provider_payload_response.py,sha256=b0lkt0rK88ARQaMWn9MAHeWtMBsZKofDMlOAUsQvv7g,818
273
274
  vellum/client/types/deployment_provider_payload_response_payload.py,sha256=xHLQnWFN0AZRZdrOiKawwpoKK7BTmnZfp0P7FCc2ZqE,188
274
- vellum/client/types/deployment_read.py,sha256=TYhgfS9rU_pyLfdSq7Ek-6WAMLEiryGJkB6Z-sZaj7A,1975
275
+ vellum/client/types/deployment_read.py,sha256=l6cy00p4y7JncJ-BwVl_NdCVWPNRjZqFz6DdL2vTTqY,1977
275
276
  vellum/client/types/deployment_release_tag_deployment_history_item.py,sha256=df4qKHT1f-z0jnRS4UmP8MQe6u3PwYej_d8KDF7EL88,631
276
277
  vellum/client/types/deployment_release_tag_read.py,sha256=dUrTOz9LH1gAvC_ktMB_7NztkeBnlNSX_9x15Ld3D3I,1278
277
278
  vellum/client/types/docker_service_token.py,sha256=T0icNHBKsIs6TrEiDRjckM_f37hcF1DMwEE8161tTvY,614
@@ -565,7 +566,7 @@ vellum/client/types/slim_document.py,sha256=HJiymYPvRxfxhBUkD8epW0hQ2Vt9PQtv398Q
565
566
  vellum/client/types/slim_document_document_to_document_index.py,sha256=vo7WbRRzbApQxT0MZu_NkjQmsFD8LoezmyeKBeGZpI8,1346
566
567
  vellum/client/types/slim_release_review.py,sha256=7DXmD1AVa_Wj7e0qiR7GUN9cSqwkk1JloYmp_3oluQQ,783
567
568
  vellum/client/types/slim_workflow_deployment.py,sha256=Js-ycMFZ8-kNFPsd4bZew9nI_eN2M_58LzDHeCjkfTg,2009
568
- vellum/client/types/slim_workflow_execution_read.py,sha256=yZvOqC9z9qa3jlnRgrSlSybwPeY-cX-xuTmZXkPVops,1968
569
+ vellum/client/types/slim_workflow_execution_read.py,sha256=Opm1HTYVMz_D2USQCB-5ZoJ4EjKKfrDhoXc0hETldVM,1936
569
570
  vellum/client/types/span_link.py,sha256=2NISI8V94W0MeIdos7aSKFmpVJgEEunuSEnKlWTUH5c,1353
570
571
  vellum/client/types/span_link_type_enum.py,sha256=NaBXnHnOKMZvgHfjhwJJNqM4wuTOxtGkMIXjN2hU-6A,130
571
572
  vellum/client/types/streaming_ad_hoc_execute_prompt_event.py,sha256=NdgmJ3AZMp6io-whZIGnGb49aiqz6__KafsrzjEF_9o,1183
@@ -717,7 +718,7 @@ vellum/client/types/workflow_deployment_release_workflow_deployment.py,sha256=ir
717
718
  vellum/client/types/workflow_deployment_release_workflow_version.py,sha256=V1Eb3goBX2lle851LkhR1tbCFa0z_O-yhMuQWCN6c-g,773
718
719
  vellum/client/types/workflow_error.py,sha256=EQajkEmLS64T0wYm0goHQl0rT7Lguurk8pLwkhjsgAI,282
719
720
  vellum/client/types/workflow_event_error.py,sha256=HIewu_kh3KNPpWegAQArvAGHCp-cBIXqlUAAc_dBZhc,687
720
- vellum/client/types/workflow_event_execution_read.py,sha256=S3o33XQYfo4JPmkXUJ-ZxCqvLfcemFeWrmOc4SGzV74,2040
721
+ vellum/client/types/workflow_event_execution_read.py,sha256=TQaBs2ZkOOJOjCkdmgI9ZX7c4XgIaNIBozCmZlOoZp8,2008
721
722
  vellum/client/types/workflow_execution_actual.py,sha256=YL5WL4O4CyaZWSrxqpE4chJ28EJlyScj5JeaLttegEg,843
722
723
  vellum/client/types/workflow_execution_actual_chat_history_request.py,sha256=L6U8tgM7SiU4qGJMZChFzj6HfHgO-YAlTXfbT7ZIaE4,1993
723
724
  vellum/client/types/workflow_execution_actual_json_request.py,sha256=5QYaPCSOwFnjH_kTrB2bTznTMFExSZdBhTkmelf1h4Q,1931
@@ -741,7 +742,9 @@ vellum/client/types/workflow_execution_span.py,sha256=3vHEx3k31fLacQaV2iMT-vibjw
741
742
  vellum/client/types/workflow_execution_span_attributes.py,sha256=SRW7mD-6uS8eA57EU6q2thBqSFfJSH4tXI9H3szZwYQ,575
742
743
  vellum/client/types/workflow_execution_streaming_body.py,sha256=phni9pJKkFGsQ5IcS4ogtG1EjBzpdBiYpN9CPbkjxH8,746
743
744
  vellum/client/types/workflow_execution_streaming_event.py,sha256=_xazehYJ5ZwTDc5cC7rpX7nDir_4glPOnx9c9ZPod1g,1551
744
- vellum/client/types/workflow_execution_usage_calculation_fulfilled_body.py,sha256=TyEIbdTWfvRjS3TGx44Mwttc274gRzLB9YjvkE0BmTw,729
745
+ vellum/client/types/workflow_execution_usage_calculation_error.py,sha256=O44pAaA3pOpn6dqxfsAFx4QnXeVfIggGWbgmTzQV4Ms,754
746
+ vellum/client/types/workflow_execution_usage_calculation_error_code_enum.py,sha256=O8CGCaWKuhnRjmQXTsw4gQn5hfDFiKED8bJYzP8R0LM,258
747
+ vellum/client/types/workflow_execution_usage_result.py,sha256=wZdRqzG_Lwi8gqDHGEU6ayx3pvO9oe8hIPzDDePELz8,928
745
748
  vellum/client/types/workflow_execution_view_online_eval_metric_result.py,sha256=1qxIHqBRhEMfbOhBpztOPhcH3mqe7bfezO7PUHUfDg4,759
746
749
  vellum/client/types/workflow_execution_workflow_result_event.py,sha256=gjyXmojwtAOtAzpILpFkJB9tM02okSXqYRw9-3rTVDA,939
747
750
  vellum/client/types/workflow_expand_meta_request.py,sha256=-6I1Zveo3wFJEWbmSsN9OiOI7ekCJxF4xEL_ApB6XE8,1151
@@ -1415,7 +1418,9 @@ vellum/types/workflow_execution_span.py,sha256=qnyiKgPrKKl0lvVtmpm09zOMTWKDBZ9nD
1415
1418
  vellum/types/workflow_execution_span_attributes.py,sha256=LCyfr-e1lwrTpMge9agbjtVDxt1l1FTZK8koMqxy6VM,172
1416
1419
  vellum/types/workflow_execution_streaming_body.py,sha256=MjkV8RLMtFj1r0yGPIuhf4Dz_EFeVU2tN8Oz6-cPssQ,171
1417
1420
  vellum/types/workflow_execution_streaming_event.py,sha256=XDEyaGRMfR9cpaRJ-WCb0O-o4LAdoMPCbjRLdYI1r-0,172
1418
- vellum/types/workflow_execution_usage_calculation_fulfilled_body.py,sha256=Vabch9oglYZehKyAvOuAKhbASKAoLWqK0dg7pqaa3s4,189
1421
+ vellum/types/workflow_execution_usage_calculation_error.py,sha256=tMF1PYRRKjC0N-GL_Az2A8Mv-1H8tS3oln_NPh6k3zo,180
1422
+ vellum/types/workflow_execution_usage_calculation_error_code_enum.py,sha256=HMqRue9gC5NRgbqt_FhANKGeNz8Vgu9fjZpEvRTek9o,190
1423
+ vellum/types/workflow_execution_usage_result.py,sha256=SrVrcv5xngwbS-6eCWU4XJ9QyyUwGJ1mPfgHPOhNqbs,169
1419
1424
  vellum/types/workflow_execution_view_online_eval_metric_result.py,sha256=dLnk3CfQOAeYIO5kiR0U8SY4JcqFWvVYwkDL-LkA5Co,187
1420
1425
  vellum/types/workflow_execution_workflow_result_event.py,sha256=3bNxtChEfqny5eEazFcGmqQpmC8283Q0J2hxLje-uks,178
1421
1426
  vellum/types/workflow_expand_meta_request.py,sha256=Ahb7gjZekJ5qI0HGHLCbn3hCpkX08EnzeXEH_3ivB4E,166
@@ -1551,9 +1556,9 @@ vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=rgcjc3gaCEX9u
1551
1556
  vellum/workflows/nodes/core/inline_subworkflow_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1552
1557
  vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py,sha256=kUqwcRMMxjTHALbwGUXCJT_aJBrHS1bkg49oL8R0JO8,4337
1553
1558
  vellum/workflows/nodes/core/map_node/__init__.py,sha256=MXpZYmGfhsMJHqqlpd64WiJRtbAtAMQz-_3fCU_cLV0,56
1554
- vellum/workflows/nodes/core/map_node/node.py,sha256=bmI48fv-UAmS14ZVV4VWrIUIYFytYGIVM6Gm7yFz8pM,9288
1559
+ vellum/workflows/nodes/core/map_node/node.py,sha256=rbF7fLAU0vUDEpgtWqeQTZFlhWOhJw38tgxWJ6exud8,9313
1555
1560
  vellum/workflows/nodes/core/map_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1556
- vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=A_M4WfdThNO5FdB7nyCyyVQBEeJWQaxmIDvvSrtO96A,5324
1561
+ vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=f3lSPYAU1vJUCLCujNOo0EAeBbOM9hnY5A1Wy58korc,6905
1557
1562
  vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
1558
1563
  vellum/workflows/nodes/core/retry_node/node.py,sha256=abtGvinLfi1tKqYIsWQKZtBUisF2Qw2yT1YoPw9cVk4,5297
1559
1564
  vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1668,7 +1673,7 @@ vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2
1668
1673
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1669
1674
  vellum/workflows/state/base.py,sha256=ZXDmVafs6sExcbx1azrZjEGQsmuY68mSRWfI7t2PT4c,22330
1670
1675
  vellum/workflows/state/context.py,sha256=KOAI1wEGn8dGmhmAemJaf4SZbitP3jpIBcwKfznQaRE,3076
1671
- vellum/workflows/state/encoder.py,sha256=TnOQojc5lTQ83g9QbpA4UCqShJvutmTMxbpKt-9gNe4,1911
1676
+ vellum/workflows/state/encoder.py,sha256=z7Mk6jQC-92wCj6XTK7VEnJ8px_lU8qy0BINqwGDN4I,2063
1672
1677
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
1673
1678
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1674
1679
  vellum/workflows/state/tests/test_state.py,sha256=YOiC9qZAzkdiqb7nRarNWeDwxo7xHv3y3czlHl81ezg,6741
@@ -1696,13 +1701,13 @@ vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrg
1696
1701
  vellum/workflows/utils/vellum_variables.py,sha256=UiGlUh0a8vel2FbW3w-xbHxSv_jNutkDdqMVtP_b42A,3385
1697
1702
  vellum/workflows/vellum_client.py,sha256=xkfoucodxNK5JR2-lbRqZx3xzDgExWkP6kySrpi_Ubc,1079
1698
1703
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1699
- vellum/workflows/workflows/base.py,sha256=9dGZzoXbVVtw19pVhmawIA1wd1iLQttAKypuVFWb0fU,23793
1704
+ vellum/workflows/workflows/base.py,sha256=V60RZat8mG0XmMuIjprkHnacD_MpUdxGcN9t4TaP_Pg,24044
1700
1705
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1701
1706
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1702
1707
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=8P5YIsNMO78_CR1NNK6wkEdkMB4b3Q_Ni1qxh78OnHo,20481
1703
1708
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1704
- vellum_ai-0.14.49.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1705
- vellum_ai-0.14.49.dist-info/METADATA,sha256=UnFisKY5VZ2o5md6oV2OU-gTx6Y24oQd4u5Bv_rJfag,5484
1706
- vellum_ai-0.14.49.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1707
- vellum_ai-0.14.49.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1708
- vellum_ai-0.14.49.dist-info/RECORD,,
1709
+ vellum_ai-0.14.50.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1710
+ vellum_ai-0.14.50.dist-info/METADATA,sha256=E3j3kzjmM-9HAa1xAKCgCf5JsoBVzbm7vadnxywpahY,5484
1711
+ vellum_ai-0.14.50.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1712
+ vellum_ai-0.14.50.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1713
+ vellum_ai-0.14.50.dist-info/RECORD,,
@@ -44,18 +44,40 @@ if TYPE_CHECKING:
44
44
  _NodeDisplayAttrType = TypeVar("_NodeDisplayAttrType")
45
45
 
46
46
 
47
+ def _get_node_input_ids_by_ref(node_class: Type[BaseNode], path: str, inst: Any):
48
+ if isinstance(inst, dict):
49
+ node_input_ids_by_name: Dict[str, UUID] = {}
50
+ for key, value in inst.items():
51
+ node_input_ids_by_name.update(_get_node_input_ids_by_ref(node_class, f"{path}.{key}", value))
52
+ return node_input_ids_by_name
53
+
54
+ return {path: uuid4_from_hash(f"{node_class.__id__}|{path}")}
55
+
56
+
47
57
  class BaseNodeDisplayMeta(type):
48
58
  def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
49
59
  cls = cast(Type["BaseNodeDisplay"], super().__new__(mcs, name, bases, dct))
60
+ # This cast shouldn't be necessary, but it's a workaround for a mypy bug
61
+ node_class = cast(Type[BaseNode], cls.infer_node_class() if name != "BaseNodeDisplay" else BaseNode)
50
62
 
51
63
  if not dct.get("output_display"):
52
- node_class = cls.infer_node_class()
53
64
  cls.output_display = {
54
65
  ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
55
66
  for ref in node_class.Outputs
56
67
  if ref.name in node_class.__output_ids__
57
68
  }
58
69
 
70
+ if not dct.get("node_input_ids_by_name"):
71
+ node_input_ids_by_name: Dict[str, UUID] = {}
72
+ for ref in node_class:
73
+ if ref not in cls.__serializable_inputs__:
74
+ continue
75
+
76
+ node_input_ids_by_name.update(_get_node_input_ids_by_ref(node_class, ref.name, ref.instance))
77
+
78
+ if node_input_ids_by_name:
79
+ cls.node_input_ids_by_name = node_input_ids_by_name
80
+
59
81
  return cls.__annotate_node__()
60
82
 
61
83
  def __annotate_node__(cls):
@@ -1,9 +1,7 @@
1
1
  import types
2
- from uuid import UUID
3
- from typing import TYPE_CHECKING, Any, Dict, Generic, Type, TypeVar
2
+ from typing import TYPE_CHECKING, Generic, Type, TypeVar
4
3
 
5
4
  from vellum.workflows.types.generics import NodeType
6
- from vellum.workflows.utils.uuids import uuid4_from_hash
7
5
  from vellum_ee.workflows.display.utils.registry import get_from_node_display_registry
8
6
 
9
7
  if TYPE_CHECKING:
@@ -22,30 +20,9 @@ def get_node_display_class(node_class: Type[NodeType]) -> Type["BaseNodeDisplay"
22
20
  # `base_node_display_class` is always a Generic class, so it's safe to index into it
23
21
  NodeDisplayBaseClass = base_node_display_class[_NodeClassType] # type: ignore[index]
24
22
 
25
- def _get_node_input_ids_by_ref(path: str, inst: Any):
26
- if isinstance(inst, dict):
27
- node_input_ids_by_name: Dict[str, UUID] = {}
28
- for key, value in inst.items():
29
- node_input_ids_by_name.update(_get_node_input_ids_by_ref(f"{path}.{key}", value))
30
- return node_input_ids_by_name
31
-
32
- return {path: uuid4_from_hash(f"{node_class.__id__}|{path}")}
33
-
34
- def exec_body(ns: Dict):
35
- node_input_ids_by_name: Dict[str, UUID] = {}
36
- for ref in node_class:
37
- if ref not in base_node_display_class.__serializable_inputs__:
38
- continue
39
-
40
- node_input_ids_by_name.update(_get_node_input_ids_by_ref(ref.name, ref.instance))
41
-
42
- if node_input_ids_by_name:
43
- ns["node_input_ids_by_name"] = node_input_ids_by_name
44
-
45
23
  NodeDisplayClass = types.new_class(
46
24
  f"{node_class.__name__}Display",
47
25
  bases=(NodeDisplayBaseClass, Generic[_NodeClassType]),
48
- exec_body=exec_body,
49
26
  )
50
27
 
51
28
  return NodeDisplayClass
@@ -0,0 +1,177 @@
1
+ from deepdiff import DeepDiff
2
+
3
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
4
+
5
+ from tests.workflows.basic_tool_calling_node.workflow import BasicToolCallingNodeWorkflow
6
+
7
+
8
+ def test_serialize_workflow():
9
+ # GIVEN a Workflow that uses a generic node
10
+ # WHEN we serialize it
11
+ workflow_display = get_workflow_display(workflow_class=BasicToolCallingNodeWorkflow)
12
+
13
+ serialized_workflow: dict = workflow_display.serialize()
14
+ # THEN we should get a serialized representation of the Workflow
15
+ assert serialized_workflow.keys() == {
16
+ "workflow_raw_data",
17
+ "input_variables",
18
+ "state_variables",
19
+ "output_variables",
20
+ }
21
+
22
+ # AND its input variables should be what we expect
23
+ input_variables = serialized_workflow["input_variables"]
24
+ assert len(input_variables) == 0
25
+
26
+ # AND its output variables should be what we expect
27
+ output_variables = serialized_workflow["output_variables"]
28
+ assert len(output_variables) == 2
29
+ assert not DeepDiff(
30
+ [
31
+ {"id": "8e7c0147-930d-4b7f-b6b1-6d79641cd3eb", "key": "text", "type": "STRING"},
32
+ {"id": "01a07e6d-7269-4f45-8b44-ef0227a2e88d", "key": "chat_history", "type": "CHAT_HISTORY"},
33
+ ],
34
+ output_variables,
35
+ ignore_order=True,
36
+ )
37
+
38
+ # AND its raw data should be what we expect
39
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
40
+ tool_calling_node = workflow_raw_data["nodes"][1]
41
+ assert tool_calling_node == {
42
+ "id": "21f29cac-da87-495f-bba1-093d423f4e46",
43
+ "label": "GetCurrentWeatherNode",
44
+ "type": "GENERIC",
45
+ "display_data": {
46
+ "position": {"x": 0.0, "y": 0.0},
47
+ "comment": {"value": "\n A tool calling node that calls the get_current_weather function.\n "},
48
+ },
49
+ "base": {
50
+ "name": "ToolCallingNode",
51
+ "module": ["vellum", "workflows", "nodes", "experimental", "tool_calling_node", "node"],
52
+ },
53
+ "definition": {
54
+ "name": "GetCurrentWeatherNode",
55
+ "module": ["tests", "workflows", "basic_tool_calling_node", "workflow"],
56
+ },
57
+ "trigger": {"id": "2414743b-b1dd-4552-8abf-9b7481df9762", "merge_behavior": "AWAIT_ATTRIBUTES"},
58
+ "ports": [{"id": "3cd6d78c-9dad-42aa-ad38-31f67057c379", "name": "default", "type": "DEFAULT"}],
59
+ "adornments": None,
60
+ "attributes": [
61
+ {
62
+ "id": "44420e39-966f-4c59-bdf8-6365a61c5d2a",
63
+ "name": "ml_model",
64
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "gpt-4o-mini"}},
65
+ },
66
+ {
67
+ "id": "669cfb4b-8c25-460e-8952-b63d91302cbc",
68
+ "name": "blocks",
69
+ "value": {
70
+ "type": "CONSTANT_VALUE",
71
+ "value": {
72
+ "type": "JSON",
73
+ "value": [
74
+ {
75
+ "block_type": "CHAT_MESSAGE",
76
+ "state": None,
77
+ "cache_config": None,
78
+ "chat_role": "SYSTEM",
79
+ "chat_source": None,
80
+ "chat_message_unterminated": None,
81
+ "blocks": [
82
+ {
83
+ "block_type": "RICH_TEXT",
84
+ "state": None,
85
+ "cache_config": None,
86
+ "blocks": [
87
+ {
88
+ "block_type": "PLAIN_TEXT",
89
+ "state": None,
90
+ "cache_config": None,
91
+ "text": "You are a weather expert",
92
+ }
93
+ ],
94
+ }
95
+ ],
96
+ },
97
+ {
98
+ "block_type": "CHAT_MESSAGE",
99
+ "state": None,
100
+ "cache_config": None,
101
+ "chat_role": "USER",
102
+ "chat_source": None,
103
+ "chat_message_unterminated": None,
104
+ "blocks": [
105
+ {
106
+ "block_type": "RICH_TEXT",
107
+ "state": None,
108
+ "cache_config": None,
109
+ "blocks": [
110
+ {
111
+ "block_type": "VARIABLE",
112
+ "state": None,
113
+ "cache_config": None,
114
+ "input_variable": "question",
115
+ }
116
+ ],
117
+ }
118
+ ],
119
+ },
120
+ ],
121
+ },
122
+ },
123
+ },
124
+ {
125
+ "id": "78324739-ff89-47a5-902b-10da0cb95c6d",
126
+ "name": "functions",
127
+ "value": {
128
+ "type": "CONSTANT_VALUE",
129
+ "value": {
130
+ "type": "JSON",
131
+ "value": [
132
+ {
133
+ "state": None,
134
+ "cache_config": None,
135
+ "name": "get_current_weather",
136
+ "description": None,
137
+ "parameters": {
138
+ "type": "object",
139
+ "properties": {"location": {"type": "string"}, "unit": {"type": "string"}},
140
+ "required": ["location", "unit"],
141
+ },
142
+ "forced": None,
143
+ "strict": None,
144
+ }
145
+ ],
146
+ },
147
+ },
148
+ },
149
+ {
150
+ "id": "0f6dc102-3460-4963-91fa-7ba85d65ef7a",
151
+ "name": "prompt_inputs",
152
+ "value": {
153
+ "type": "CONSTANT_VALUE",
154
+ "value": {"type": "JSON", "value": {"question": "What's the weather like in San Francisco?"}},
155
+ },
156
+ },
157
+ {
158
+ "id": "5c041b7d-732c-4773-a93a-32211f2af0b3",
159
+ "name": "max_tool_calls",
160
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 1.0}},
161
+ },
162
+ ],
163
+ "outputs": [
164
+ {
165
+ "id": "e62bc785-a914-4066-b79e-8c89a5d0ec6c",
166
+ "name": "text",
167
+ "type": "STRING",
168
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": ""}},
169
+ },
170
+ {
171
+ "id": "4674f1d9-e3af-411f-8a55-40a3a3ab5394",
172
+ "name": "chat_history",
173
+ "type": "CHAT_HISTORY",
174
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": []}},
175
+ },
176
+ ],
177
+ }
@@ -253,7 +253,7 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
253
253
  "type": "CONSTANT_VALUE",
254
254
  "value": {
255
255
  "type": "JSON",
256
- "items": constant_values,
256
+ "value": constant_values,
257
257
  },
258
258
  }
259
259
  else:
@@ -153,20 +153,8 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
153
153
  "definition": None,
154
154
  }
155
155
 
156
- # Add all the nodes in the workflow
157
- for node in self._workflow.get_nodes():
158
- node_display = self.display_context.node_displays[node]
159
-
160
- try:
161
- serialized_node = node_display.serialize(self.display_context)
162
- except NotImplementedError as e:
163
- self.add_error(e)
164
- continue
165
-
166
- serialized_nodes[node_display.node_id] = serialized_node
167
-
168
- # Add all unused nodes in the workflow
169
- for node in self._workflow.get_unused_nodes():
156
+ # Add all the nodes in the workflows
157
+ for node in self._workflow.get_all_nodes():
170
158
  node_display = self.display_context.node_displays[node]
171
159
 
172
160
  try:
@@ -417,16 +405,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
417
405
 
418
406
  port_displays: PortDisplays = {}
419
407
 
420
- for node in self._workflow.get_nodes():
421
- self._enrich_node_displays(
422
- node=node,
423
- node_displays=node_displays,
424
- global_node_displays=global_node_displays,
425
- global_node_output_displays=global_node_output_displays,
426
- port_displays=port_displays,
427
- )
428
-
429
- for node in self._workflow.get_unused_nodes():
408
+ for node in self._workflow.get_all_nodes():
430
409
  self._enrich_node_displays(
431
410
  node=node,
432
411
  node_displays=node_displays,
@@ -466,7 +466,7 @@ def test_serialize_workflow__array_values():
466
466
  assert "value" in array_output
467
467
  assert array_output["value"] == {
468
468
  "type": "CONSTANT_VALUE",
469
- "value": {"type": "JSON", "items": ["item1", "item2", "item3"]},
469
+ "value": {"type": "JSON", "value": ["item1", "item2", "item3"]},
470
470
  }
471
471
 
472
472
  nested_array_outputs = [val for val in outputs if isinstance(val, dict) and val["name"] == "nested_array_value"]
@@ -477,7 +477,7 @@ def test_serialize_workflow__array_values():
477
477
  assert "value" in nested_array_output
478
478
  assert nested_array_output["value"] == {
479
479
  "type": "CONSTANT_VALUE",
480
- "value": {"type": "JSON", "items": [["item1", "item2", "item3"], ["item4", "item5", "item6"]]},
480
+ "value": {"type": "JSON", "value": [["item1", "item2", "item3"], ["item4", "item5", "item6"]]},
481
481
  }
482
482
 
483
483
  mixed_array_outputs = [val for val in outputs if isinstance(val, dict) and val["name"] == "mixed_array_value"]
@@ -488,7 +488,7 @@ def test_serialize_workflow__array_values():
488
488
  assert "value" in mixed_array_output
489
489
  assert mixed_array_output["value"] == {
490
490
  "type": "CONSTANT_VALUE",
491
- "value": {"type": "JSON", "items": [["item1"], "item2", "item3"]},
491
+ "value": {"type": "JSON", "value": [["item1"], "item2", "item3"]},
492
492
  }
493
493
 
494
494