vellum-ai 0.12.13__py3-none-any.whl → 0.12.14__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
vellum/__init__.py CHANGED
@@ -1,4 +1,7 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
+ from .plugins.utils import load_runtime_plugins
3
+
4
+ load_runtime_plugins()
2
5
 
3
6
  from .types import (
4
7
  AdHocExecutePromptEvent,
@@ -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.12.13",
21
+ "X-Fern-SDK-Version": "0.12.14",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -1,3 +1,4 @@
1
+ from functools import lru_cache
1
2
  from typing import Any, Dict, Literal, Optional, Tuple, Union
2
3
 
3
4
  from pydantic.plugin import (
@@ -10,12 +11,20 @@ from pydantic.plugin import (
10
11
  )
11
12
  from pydantic_core import CoreSchema
12
13
 
13
- from vellum.workflows.descriptors.base import BaseDescriptor
14
+
15
+ @lru_cache(maxsize=1)
16
+ def import_base_descriptor():
17
+ """
18
+ We have to avoid importing from vellum.* in this file because it will cause a circular import.
19
+ """
20
+ from vellum.workflows.descriptors.base import BaseDescriptor
21
+
22
+ return BaseDescriptor
14
23
 
15
24
 
16
25
  # https://docs.pydantic.dev/2.8/concepts/plugins/#build-a-plugin
17
26
  class OnValidatePython(ValidatePythonHandlerProtocol):
18
- tracked_descriptors: Dict[str, BaseDescriptor] = {}
27
+ tracked_descriptors: Dict[str, Any] = {}
19
28
 
20
29
  def on_enter(
21
30
  self,
@@ -31,6 +40,7 @@ class OnValidatePython(ValidatePythonHandlerProtocol):
31
40
  return
32
41
 
33
42
  self.tracked_descriptors = {}
43
+ BaseDescriptor = import_base_descriptor()
34
44
 
35
45
  for key, value in input.items():
36
46
  if isinstance(value, BaseDescriptor):
@@ -2,6 +2,7 @@ import pytest
2
2
 
3
3
  from vellum.workflows.descriptors.utils import resolve_value
4
4
  from vellum.workflows.nodes.bases.base import BaseNode
5
+ from vellum.workflows.references.constant import ConstantValueReference
5
6
  from vellum.workflows.state.base import BaseState
6
7
 
7
8
 
@@ -73,6 +74,7 @@ class DummyNode(BaseNode[FixtureState]):
73
74
  True,
74
75
  ),
75
76
  (FixtureState.zeta["foo"], "bar"),
77
+ (ConstantValueReference(1), 1),
76
78
  ],
77
79
  ids=[
78
80
  "or",
@@ -116,6 +118,7 @@ class DummyNode(BaseNode[FixtureState]):
116
118
  "is_not_blank",
117
119
  "or_and",
118
120
  "accessor",
121
+ "constants",
119
122
  ],
120
123
  )
121
124
  def test_resolve_value__happy_path(descriptor, expected_value):
@@ -214,6 +214,9 @@ class _BaseNodeExecutionMeta(type):
214
214
  return self_execution_class.node_class.__name__ == other_execution_class.node_class.__name__
215
215
 
216
216
 
217
+ NodeRunResponse = Union[BaseOutputs, Iterator[BaseOutput]]
218
+
219
+
217
220
  class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
218
221
  __id__: UUID = uuid4_from_hash(__qualname__)
219
222
  state: StateType
@@ -350,7 +353,7 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
350
353
 
351
354
  self._inputs = MappingProxyType(all_inputs)
352
355
 
353
- def run(self) -> Union[BaseOutputs, Iterator[BaseOutput]]:
356
+ def run(self) -> NodeRunResponse:
354
357
  return self.Outputs()
355
358
 
356
359
  def __repr__(self) -> str:
@@ -0,0 +1,75 @@
1
+ from typing import TYPE_CHECKING, Any, Dict, Generic, Optional, Tuple, Type
2
+
3
+ from vellum.workflows.nodes.bases.base import BaseNode, BaseNodeMeta
4
+ from vellum.workflows.outputs.base import BaseOutputs
5
+ from vellum.workflows.references.output import OutputReference
6
+ from vellum.workflows.types.generics import StateType
7
+
8
+ if TYPE_CHECKING:
9
+ from vellum.workflows import BaseWorkflow
10
+
11
+
12
+ class _BaseAdornmentNodeMeta(BaseNodeMeta):
13
+ def __new__(cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
14
+ node_class = super().__new__(cls, name, bases, dct)
15
+
16
+ subworkflow_attribute = dct.get("subworkflow")
17
+ if not subworkflow_attribute:
18
+ return node_class
19
+
20
+ if not issubclass(node_class, BaseAdornmentNode):
21
+ raise ValueError("BaseAdornableNodeMeta can only be used on subclasses of BaseAdornableNode")
22
+
23
+ subworkflow_outputs = getattr(subworkflow_attribute, "Outputs")
24
+ if not issubclass(subworkflow_outputs, BaseOutputs):
25
+ raise ValueError("subworkflow.Outputs must be a subclass of BaseOutputs")
26
+
27
+ outputs_class = dct.get("Outputs")
28
+ if not outputs_class:
29
+ raise ValueError("Outputs class not found in base classes")
30
+
31
+ if not issubclass(outputs_class, BaseNode.Outputs):
32
+ raise ValueError("Outputs class must be a subclass of BaseNode.Outputs")
33
+
34
+ for descriptor in subworkflow_outputs:
35
+ node_class.__annotate_outputs_class__(outputs_class, descriptor)
36
+
37
+ return node_class
38
+
39
+ def __getattribute__(cls, name: str) -> Any:
40
+ try:
41
+ return super().__getattribute__(name)
42
+ except AttributeError:
43
+ if name != "__wrapped_node__" and issubclass(cls, BaseAdornmentNode):
44
+ return getattr(cls.__wrapped_node__, name)
45
+ raise
46
+
47
+ @property
48
+ def _localns(cls) -> Dict[str, Any]:
49
+ if not hasattr(cls, "SubworkflowInputs"):
50
+ return super()._localns
51
+
52
+ return {
53
+ **super()._localns,
54
+ "SubworkflowInputs": getattr(cls, "SubworkflowInputs"),
55
+ }
56
+
57
+
58
+ class BaseAdornmentNode(
59
+ BaseNode[StateType],
60
+ Generic[StateType],
61
+ metaclass=_BaseAdornmentNodeMeta,
62
+ ):
63
+ """
64
+ A base node that enables the node to be used as an adornment - meaning it can wrap another node. The
65
+ wrapped node is stored in the `__wrapped_node__` attribute and is redefined as a single-node subworkflow.
66
+ """
67
+
68
+ __wrapped_node__: Optional[Type["BaseNode"]] = None
69
+ subworkflow: Type["BaseWorkflow"]
70
+
71
+ @classmethod
72
+ def __annotate_outputs_class__(cls, outputs_class: Type[BaseOutputs], reference: OutputReference) -> None:
73
+ # Subclasses of BaseAdornableNode can override this method to provider their own
74
+ # approach to annotating the outputs class based on the `subworkflow.Outputs`
75
+ setattr(outputs_class, reference.name, reference)
@@ -1,6 +1,7 @@
1
1
  from uuid import UUID
2
2
  from typing import Optional
3
3
 
4
+ from vellum.client.types.string_vellum_value_request import StringVellumValueRequest
4
5
  from vellum.core.pydantic_utilities import UniversalBaseModel
5
6
  from vellum.workflows.inputs.base import BaseInputs
6
7
  from vellum.workflows.nodes.bases.base import BaseNode
@@ -135,3 +136,15 @@ def test_base_node__default_id():
135
136
 
136
137
  # THEN it should equal the hash of `test_base_node__default_id.<locals>.MyNode`
137
138
  assert my_id == UUID("8e71bea7-ce68-492f-9abe-477c788e6273")
139
+
140
+
141
+ def test_base_node__node_resolution__descriptor_in_fern_pydantic():
142
+ class State(BaseState):
143
+ foo: str
144
+
145
+ class SomeNode(BaseNode):
146
+ model = StringVellumValueRequest(value=State.foo)
147
+
148
+ node = SomeNode(state=State(foo="bar"))
149
+
150
+ assert node.model.value == "bar"
@@ -10,6 +10,7 @@ from vellum.workflows.state.base import BaseState
10
10
  from vellum.workflows.state.context import WorkflowContext
11
11
  from vellum.workflows.types.core import EntityInputsInterface
12
12
  from vellum.workflows.types.generics import StateType, WorkflowInputsType
13
+ from vellum.workflows.workflows.event_filters import all_workflow_event_filter
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from vellum.workflows.workflows.base import BaseWorkflow
@@ -36,6 +37,7 @@ class InlineSubworkflowNode(BaseNode[StateType], Generic[StateType, WorkflowInpu
36
37
  )
37
38
  subworkflow_stream = subworkflow.stream(
38
39
  inputs=self._compile_subworkflow_inputs(),
40
+ event_filter=all_workflow_event_filter,
39
41
  )
40
42
 
41
43
  outputs: Optional[BaseOutputs] = None
@@ -9,21 +9,21 @@ from vellum.workflows.errors.types import WorkflowErrorCode
9
9
  from vellum.workflows.events.types import ParentContext
10
10
  from vellum.workflows.exceptions import NodeException
11
11
  from vellum.workflows.inputs.base import BaseInputs
12
- from vellum.workflows.nodes.bases import BaseNode
12
+ from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
13
+ from vellum.workflows.nodes.utils import create_adornment
13
14
  from vellum.workflows.outputs import BaseOutputs
14
- from vellum.workflows.state.base import BaseState
15
+ from vellum.workflows.references.output import OutputReference
15
16
  from vellum.workflows.state.context import WorkflowContext
16
- from vellum.workflows.types.generics import NodeType, StateType
17
+ from vellum.workflows.types.generics import StateType
17
18
  from vellum.workflows.workflows.event_filters import all_workflow_event_filter
18
19
 
19
20
  if TYPE_CHECKING:
20
- from vellum.workflows import BaseWorkflow
21
21
  from vellum.workflows.events.workflow import WorkflowEvent
22
22
 
23
23
  MapNodeItemType = TypeVar("MapNodeItemType")
24
24
 
25
25
 
26
- class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
26
+ class MapNode(BaseAdornmentNode[StateType], Generic[StateType, MapNodeItemType]):
27
27
  """
28
28
  Used to map over a list of items and execute a Subworkflow on each iteration.
29
29
 
@@ -33,11 +33,10 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
33
33
  """
34
34
 
35
35
  items: List[MapNodeItemType]
36
- subworkflow: Type["BaseWorkflow"]
37
36
  concurrency: Optional[int] = None
38
37
 
39
- class Outputs(BaseOutputs):
40
- mapped_items: list
38
+ class Outputs(BaseAdornmentNode.Outputs):
39
+ pass
41
40
 
42
41
  class SubworkflowInputs(BaseInputs):
43
42
  # TODO: Both type: ignore's below are believed to be incorrect and both have the following error:
@@ -54,6 +53,7 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
54
53
  mapped_items[output_descripter.name] = [None] * len(self.items)
55
54
 
56
55
  self._event_queue: Queue[Tuple[int, WorkflowEvent]] = Queue()
56
+ self._concurrency_queue: Queue[Thread] = Queue()
57
57
  fulfilled_iterations: List[bool] = []
58
58
  for index, item in enumerate(self.items):
59
59
  fulfilled_iterations.append(False)
@@ -66,11 +66,21 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
66
66
  "parent_context": parent_context,
67
67
  },
68
68
  )
69
- thread.start()
69
+ if self.concurrency is None:
70
+ thread.start()
71
+ else:
72
+ self._concurrency_queue.put(thread)
73
+
74
+ if self.concurrency is not None:
75
+ concurrency_count = 0
76
+ while concurrency_count < self.concurrency:
77
+ is_empty = self._start_thread()
78
+ if is_empty:
79
+ break
80
+
81
+ concurrency_count += 1
70
82
 
71
83
  try:
72
- # We should consolidate this logic with the logic workflow runner uses
73
- # https://app.shortcut.com/vellum/story/4736
74
84
  while map_node_event := self._event_queue.get():
75
85
  index = map_node_event[0]
76
86
  terminal_event = map_node_event[1]
@@ -86,6 +96,9 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
86
96
  fulfilled_iterations[index] = True
87
97
  if all(fulfilled_iterations):
88
98
  break
99
+
100
+ if self.concurrency is not None:
101
+ self._start_thread()
89
102
  elif terminal_event.name == "workflow.execution.paused":
90
103
  raise NodeException(
91
104
  code=WorkflowErrorCode.INVALID_OUTPUTS,
@@ -98,7 +111,12 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
98
111
  )
99
112
  except Empty:
100
113
  pass
101
- return self.Outputs(**mapped_items)
114
+
115
+ outputs = self.Outputs()
116
+ for output_name, output_list in mapped_items.items():
117
+ setattr(outputs, output_name, output_list)
118
+
119
+ return outputs
102
120
 
103
121
  def _context_run_subworkflow(
104
122
  self, *, item: MapNodeItemType, index: int, parent_context: Optional[ParentContext] = None
@@ -109,7 +127,10 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
109
127
 
110
128
  def _run_subworkflow(self, *, item: MapNodeItemType, index: int) -> None:
111
129
  context = WorkflowContext(vellum_client=self._context.vellum_client)
112
- subworkflow = self.subworkflow(parent_state=self.state, context=context)
130
+ subworkflow = self.subworkflow(
131
+ parent_state=self.state,
132
+ context=context,
133
+ )
113
134
  events = subworkflow.stream(
114
135
  inputs=self.SubworkflowInputs(index=index, item=item, all_items=self.items),
115
136
  event_filter=all_workflow_event_filter,
@@ -118,6 +139,14 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
118
139
  for event in events:
119
140
  self._event_queue.put((index, event))
120
141
 
142
+ def _start_thread(self) -> bool:
143
+ if self._concurrency_queue.empty():
144
+ return False
145
+
146
+ thread = self._concurrency_queue.get()
147
+ thread.start()
148
+ return True
149
+
121
150
  @overload
122
151
  @classmethod
123
152
  def wrap(cls, items: List[MapNodeItemType]) -> Callable[..., Type["MapNode[StateType, MapNodeItemType]"]]: ...
@@ -134,37 +163,12 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
134
163
  def wrap(
135
164
  cls, items: Union[List[MapNodeItemType], BaseDescriptor[List[MapNodeItemType]]]
136
165
  ) -> Callable[..., Type["MapNode[StateType, MapNodeItemType]"]]:
137
- _items = items
166
+ return create_adornment(cls, attributes={"items": items})
138
167
 
139
- def decorator(inner_cls: Type[NodeType]) -> Type["MapNode[StateType, MapNodeItemType]"]:
140
- # Investigate how to use dependency injection to avoid circular imports
141
- # https://app.shortcut.com/vellum/story/4116
142
- from vellum.workflows import BaseWorkflow
143
-
144
- class Subworkflow(BaseWorkflow[MapNode.SubworkflowInputs, BaseState]):
145
- graph = inner_cls
146
-
147
- # mypy is wrong here, this works and is defined
148
- class Outputs(inner_cls.Outputs): # type: ignore[name-defined]
149
- pass
150
-
151
- class WrappedNodeOutputs(BaseOutputs):
152
- pass
153
-
154
- WrappedNodeOutputs.__annotations__ = {
155
- # TODO: We'll need to infer the type T of Subworkflow.Outputs[name] so we could do List[T] here
156
- # https://app.shortcut.com/vellum/story/4119
157
- descriptor.name: List
158
- for descriptor in inner_cls.Outputs
159
- }
160
-
161
- class WrappedNode(MapNode[StateType, MapNodeItemType]):
162
- items = _items
163
- subworkflow = Subworkflow
164
-
165
- class Outputs(WrappedNodeOutputs):
166
- pass
167
-
168
- return WrappedNode
168
+ @classmethod
169
+ def __annotate_outputs_class__(cls, outputs_class: Type[BaseOutputs], reference: OutputReference) -> None:
170
+ parameter_type = reference.types[0]
171
+ annotation = List[parameter_type] # type: ignore[valid-type]
169
172
 
170
- return decorator
173
+ previous_annotations = {prev: annotation for prev in outputs_class.__annotations__ if not prev.startswith("_")}
174
+ outputs_class.__annotations__ = {**previous_annotations, reference.name: annotation}
@@ -1,27 +1,16 @@
1
- from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, Optional, Type
1
+ from typing import Callable, Generic, Optional, Type
2
2
 
3
3
  from vellum.workflows.errors.types import WorkflowErrorCode
4
4
  from vellum.workflows.exceptions import NodeException
5
5
  from vellum.workflows.inputs.base import BaseInputs
6
6
  from vellum.workflows.nodes.bases import BaseNode
7
- from vellum.workflows.nodes.bases.base import BaseNodeMeta
8
- from vellum.workflows.state.base import BaseState
7
+ from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
8
+ from vellum.workflows.nodes.utils import create_adornment
9
+ from vellum.workflows.state.context import WorkflowContext
9
10
  from vellum.workflows.types.generics import StateType
10
11
 
11
- if TYPE_CHECKING:
12
- from vellum.workflows import BaseWorkflow
13
12
 
14
-
15
- class _RetryNodeMeta(BaseNodeMeta):
16
- @property
17
- def _localns(cls) -> Dict[str, Any]:
18
- return {
19
- **super()._localns,
20
- "SubworkflowInputs": getattr(cls, "SubworkflowInputs"),
21
- }
22
-
23
-
24
- class RetryNode(BaseNode[StateType], Generic[StateType], metaclass=_RetryNodeMeta):
13
+ class RetryNode(BaseAdornmentNode[StateType], Generic[StateType]):
25
14
  """
26
15
  Used to retry a Subworkflow a specified number of times.
27
16
 
@@ -32,7 +21,6 @@ class RetryNode(BaseNode[StateType], Generic[StateType], metaclass=_RetryNodeMet
32
21
 
33
22
  max_attempts: int
34
23
  retry_on_error_code: Optional[WorkflowErrorCode] = None
35
- subworkflow: Type["BaseWorkflow[SubworkflowInputs, BaseState]"]
36
24
 
37
25
  class SubworkflowInputs(BaseInputs):
38
26
  attempt_number: int
@@ -41,9 +29,10 @@ class RetryNode(BaseNode[StateType], Generic[StateType], metaclass=_RetryNodeMet
41
29
  last_exception = Exception("max_attempts must be greater than 0")
42
30
  for index in range(self.max_attempts):
43
31
  attempt_number = index + 1
32
+ context = WorkflowContext(vellum_client=self._context.vellum_client)
44
33
  subworkflow = self.subworkflow(
45
34
  parent_state=self.state,
46
- context=self._context,
35
+ context=context,
47
36
  )
48
37
  terminal_event = subworkflow.run(
49
38
  inputs=self.SubworkflowInputs(attempt_number=attempt_number),
@@ -78,30 +67,6 @@ Message: {terminal_event.error.message}""",
78
67
  def wrap(
79
68
  cls, max_attempts: int, retry_on_error_code: Optional[WorkflowErrorCode] = None
80
69
  ) -> Callable[..., Type["RetryNode"]]:
81
- _max_attempts = max_attempts
82
- _retry_on_error_code = retry_on_error_code
83
-
84
- def decorator(inner_cls: Type[BaseNode]) -> Type["RetryNode"]:
85
- # Investigate how to use dependency injection to avoid circular imports
86
- # https://app.shortcut.com/vellum/story/4116
87
- from vellum.workflows import BaseWorkflow
88
-
89
- class Subworkflow(BaseWorkflow[RetryNode.SubworkflowInputs, BaseState]):
90
- graph = inner_cls
91
-
92
- # mypy is wrong here, this works and is defined
93
- class Outputs(inner_cls.Outputs): # type: ignore[name-defined]
94
- pass
95
-
96
- class WrappedNode(RetryNode[StateType]):
97
- max_attempts = _max_attempts
98
- retry_on_error_code = _retry_on_error_code
99
-
100
- subworkflow = Subworkflow
101
-
102
- class Outputs(Subworkflow.Outputs):
103
- pass
104
-
105
- return WrappedNode
106
-
107
- return decorator
70
+ return create_adornment(
71
+ cls, attributes={"max_attempts": max_attempts, "retry_on_error_code": retry_on_error_code}
72
+ )
@@ -1,61 +1,18 @@
1
- import sys
2
- from types import ModuleType
3
- from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, Iterator, Optional, Set, Tuple, Type, TypeVar
1
+ from typing import Callable, Generic, Iterator, Optional, Set, Type
4
2
 
5
3
  from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
6
4
  from vellum.workflows.exceptions import NodeException
7
5
  from vellum.workflows.nodes.bases import BaseNode
8
- from vellum.workflows.nodes.bases.base import BaseNodeMeta
9
- from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
6
+ from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
7
+ from vellum.workflows.nodes.utils import create_adornment
10
8
  from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
9
+ from vellum.workflows.references.output import OutputReference
11
10
  from vellum.workflows.state.context import WorkflowContext
12
11
  from vellum.workflows.types.generics import StateType
13
12
  from vellum.workflows.workflows.event_filters import all_workflow_event_filter
14
13
 
15
- if TYPE_CHECKING:
16
- from vellum.workflows import BaseWorkflow
17
14
 
18
- Subworkflow = Type["BaseWorkflow"]
19
- _T = TypeVar("_T", bound=BaseOutputs)
20
-
21
-
22
- class _TryNodeMeta(BaseNodeMeta):
23
- def __new__(cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
24
- node_class = super().__new__(cls, name, bases, dct)
25
-
26
- subworkflow_attribute = dct.get("subworkflow")
27
- if not subworkflow_attribute:
28
- return node_class
29
-
30
- subworkflow_outputs = getattr(subworkflow_attribute, "Outputs")
31
- if not issubclass(subworkflow_outputs, BaseOutputs):
32
- raise ValueError("subworkflow.Outputs must be a subclass of BaseOutputs")
33
-
34
- outputs_class = dct.get("Outputs")
35
- if not outputs_class:
36
- raise ValueError("Outputs class not found in base classes")
37
-
38
- if not issubclass(outputs_class, BaseNode.Outputs):
39
- raise ValueError("Outputs class must be a subclass of BaseNode.Outputs")
40
-
41
- for descriptor in subworkflow_outputs:
42
- if descriptor.name == "error":
43
- raise ValueError("`error` is a reserved name for TryNode.Outputs")
44
-
45
- setattr(outputs_class, descriptor.name, descriptor)
46
-
47
- return node_class
48
-
49
- def __getattribute__(cls, name: str) -> Any:
50
- try:
51
- return super().__getattribute__(name)
52
- except AttributeError:
53
- if name != "__wrapped_node__" and issubclass(cls, TryNode):
54
- return getattr(cls.__wrapped_node__, name)
55
- raise
56
-
57
-
58
- class TryNode(BaseNode[StateType], Generic[StateType], metaclass=_TryNodeMeta):
15
+ class TryNode(BaseAdornmentNode[StateType], Generic[StateType]):
59
16
  """
60
17
  Used to execute a Subworkflow and handle errors.
61
18
 
@@ -63,9 +20,7 @@ class TryNode(BaseNode[StateType], Generic[StateType], metaclass=_TryNodeMeta):
63
20
  subworkflow: Type["BaseWorkflow"] - The Subworkflow to execute
64
21
  """
65
22
 
66
- __wrapped_node__: Optional[Type["BaseNode"]] = None
67
23
  on_error_code: Optional[WorkflowErrorCode] = None
68
- subworkflow: Type["BaseWorkflow"]
69
24
 
70
25
  class Outputs(BaseNode.Outputs):
71
26
  error: Optional[WorkflowError] = None
@@ -129,38 +84,11 @@ Message: {event.error.message}""",
129
84
 
130
85
  @classmethod
131
86
  def wrap(cls, on_error_code: Optional[WorkflowErrorCode] = None) -> Callable[..., Type["TryNode"]]:
132
- _on_error_code = on_error_code
133
-
134
- def decorator(inner_cls: Type[BaseNode]) -> Type["TryNode"]:
135
- # Investigate how to use dependency injection to avoid circular imports
136
- # https://app.shortcut.com/vellum/story/4116
137
- from vellum.workflows import BaseWorkflow
138
-
139
- inner_cls._is_wrapped_node = True
140
-
141
- class Subworkflow(BaseWorkflow):
142
- graph = inner_cls
143
-
144
- # mypy is wrong here, this works and is defined
145
- class Outputs(inner_cls.Outputs): # type: ignore[name-defined]
146
- pass
147
-
148
- dynamic_module = f"{inner_cls.__module__}.{inner_cls.__name__}.{ADORNMENT_MODULE_NAME}"
149
- # This dynamic module allows calls to `type_hints` to work
150
- sys.modules[dynamic_module] = ModuleType(dynamic_module)
151
-
152
- # We use a dynamic wrapped node class to be uniquely tied to this `inner_cls` node during serialization
153
- WrappedNode = type(
154
- cls.__name__,
155
- (TryNode,),
156
- {
157
- "__wrapped_node__": inner_cls,
158
- "__module__": dynamic_module,
159
- "on_error_code": _on_error_code,
160
- "subworkflow": Subworkflow,
161
- "Ports": type("Ports", (TryNode.Ports,), {port.name: port.copy() for port in inner_cls.Ports}),
162
- },
163
- )
164
- return WrappedNode
87
+ return create_adornment(cls, attributes={"on_error_code": on_error_code})
88
+
89
+ @classmethod
90
+ def __annotate_outputs_class__(cls, outputs_class: Type[BaseOutputs], reference: OutputReference) -> None:
91
+ if reference.name == "error":
92
+ raise ValueError("`error` is a reserved name for TryNode.Outputs")
165
93
 
166
- return decorator
94
+ setattr(outputs_class, reference.name, reference)
@@ -1,5 +1,7 @@
1
1
  from functools import cache
2
- from typing import Type
2
+ import sys
3
+ from types import ModuleType
4
+ from typing import Any, Callable, Optional, Type, TypeVar
3
5
 
4
6
  from vellum.workflows.nodes import BaseNode
5
7
  from vellum.workflows.ports.port import Port
@@ -42,3 +44,44 @@ def has_wrapped_node(node: Type[NodeType]) -> bool:
42
44
  return False
43
45
 
44
46
  return True
47
+
48
+
49
+ AdornableNode = TypeVar("AdornableNode", bound=BaseNode)
50
+
51
+
52
+ def create_adornment(
53
+ adornable_cls: Type[AdornableNode], attributes: Optional[dict[str, Any]] = None
54
+ ) -> Callable[..., Type["AdornableNode"]]:
55
+ def decorator(inner_cls: Type[BaseNode]) -> Type["AdornableNode"]:
56
+ # Investigate how to use dependency injection to avoid circular imports
57
+ # https://app.shortcut.com/vellum/story/4116
58
+ from vellum.workflows import BaseWorkflow
59
+
60
+ inner_cls._is_wrapped_node = True
61
+
62
+ class Subworkflow(BaseWorkflow):
63
+ graph = inner_cls
64
+
65
+ # mypy is wrong here, this works and is defined
66
+ class Outputs(inner_cls.Outputs): # type: ignore[name-defined]
67
+ pass
68
+
69
+ dynamic_module = f"{inner_cls.__module__}.{inner_cls.__name__}.{ADORNMENT_MODULE_NAME}"
70
+ # This dynamic module allows calls to `type_hints` to work
71
+ sys.modules[dynamic_module] = ModuleType(dynamic_module)
72
+
73
+ # We use a dynamic wrapped node class to be uniquely tied to this `inner_cls` node during serialization
74
+ WrappedNode = type(
75
+ adornable_cls.__name__,
76
+ (adornable_cls,),
77
+ {
78
+ "__wrapped_node__": inner_cls,
79
+ "__module__": dynamic_module,
80
+ "subworkflow": Subworkflow,
81
+ "Ports": type("Ports", (adornable_cls.Ports,), {port.name: port.copy() for port in inner_cls.Ports}),
82
+ **(attributes or {}),
83
+ },
84
+ )
85
+ return WrappedNode
86
+
87
+ return decorator
@@ -0,0 +1,21 @@
1
+ from typing import TYPE_CHECKING, Generic, TypeVar
2
+
3
+ from vellum.workflows.descriptors.base import BaseDescriptor
4
+
5
+ if TYPE_CHECKING:
6
+ from vellum.workflows.state.base import BaseState
7
+
8
+ _T = TypeVar("_T")
9
+
10
+
11
+ class ConstantValueReference(BaseDescriptor[_T], Generic[_T]):
12
+ def __init__(
13
+ self,
14
+ value: _T,
15
+ ) -> None:
16
+ self._value = value
17
+ types = (type(self._value),)
18
+ super().__init__(name=str(self._value), types=types)
19
+
20
+ def resolve(self, state: "BaseState") -> _T:
21
+ return self._value
@@ -44,11 +44,13 @@ from vellum.workflows.events.workflow import (
44
44
  )
45
45
  from vellum.workflows.exceptions import NodeException
46
46
  from vellum.workflows.nodes.bases import BaseNode
47
+ from vellum.workflows.nodes.bases.base import NodeRunResponse
47
48
  from vellum.workflows.outputs import BaseOutputs
48
49
  from vellum.workflows.outputs.base import BaseOutput
49
50
  from vellum.workflows.ports.port import Port
50
51
  from vellum.workflows.references import ExternalInputReference, OutputReference
51
52
  from vellum.workflows.state.base import BaseState
53
+ from vellum.workflows.types.cycle_map import CycleMap
52
54
  from vellum.workflows.types.generics import OutputsType, StateType, WorkflowInputsType
53
55
 
54
56
  if TYPE_CHECKING:
@@ -124,9 +126,7 @@ class WorkflowRunner(Generic[StateType]):
124
126
 
125
127
  self._dependencies: Dict[Type[BaseNode], Set[Type[BaseNode]]] = defaultdict(set)
126
128
  self._state_forks: Set[StateType] = {self._initial_state}
127
- self._mocks_by_node_outputs_class = (
128
- {mock.__class__: mock for mock in node_output_mocks} if node_output_mocks else {}
129
- )
129
+ self._mocks_by_node_outputs_class = CycleMap(items=node_output_mocks or [], key_by=lambda mock: mock.__class__)
130
130
 
131
131
  self._active_nodes_by_execution_id: Dict[UUID, BaseNode[StateType]] = {}
132
132
  self._cancel_signal = cancel_signal
@@ -182,6 +182,7 @@ class WorkflowRunner(Generic[StateType]):
182
182
  node_definition=node.__class__,
183
183
  parent=parent_context,
184
184
  )
185
+ node_run_response: NodeRunResponse
185
186
  if node.Outputs not in self._mocks_by_node_outputs_class:
186
187
  with execution_context(parent_context=updated_parent_context):
187
188
  node_run_response = node.run()
@@ -0,0 +1,34 @@
1
+ from typing import Callable, Dict, Generic, List, TypeVar
2
+
3
+ _K = TypeVar("_K")
4
+ _T = TypeVar("_T")
5
+
6
+
7
+ class CycleMap(Generic[_K, _T]):
8
+ """
9
+ A map that cycles through a list of items for each key.
10
+ """
11
+
12
+ def __init__(self, items: List[_T], key_by: Callable[[_T], _K]):
13
+ self._items: Dict[_K, List[_T]] = {}
14
+ for item in items:
15
+ self._add_item(key_by(item), item)
16
+
17
+ def _add_item(self, key: _K, item: _T):
18
+ if key not in self._items:
19
+ self._items[key] = []
20
+ self._items[key].append(item)
21
+
22
+ def _get_item(self, key: _K) -> _T:
23
+ item = self._items[key].pop(0)
24
+ self._items[key].append(item)
25
+ return item
26
+
27
+ def __getitem__(self, key: _K) -> _T:
28
+ return self._get_item(key)
29
+
30
+ def __setitem__(self, key: _K, value: _T):
31
+ self._add_item(key, value)
32
+
33
+ def __contains__(self, key: _K) -> bool:
34
+ return key in self._items
@@ -1,16 +1,7 @@
1
- # flake8: noqa: E402
2
-
3
- import importlib
4
- import inspect
5
-
6
- from vellum.plugins.utils import load_runtime_plugins
7
- from vellum.workflows.utils.uuids import uuid4_from_hash
8
- from vellum.workflows.workflows.event_filters import workflow_event_filter
9
-
10
- load_runtime_plugins()
11
-
12
1
  from datetime import datetime
13
2
  from functools import lru_cache
3
+ import importlib
4
+ import inspect
14
5
  from threading import Event as ThreadingEvent
15
6
  from uuid import UUID, uuid4
16
7
  from typing import (
@@ -79,6 +70,8 @@ from vellum.workflows.state.context import WorkflowContext
79
70
  from vellum.workflows.state.store import Store
80
71
  from vellum.workflows.types.generics import StateType, WorkflowInputsType
81
72
  from vellum.workflows.types.utils import get_original_base
73
+ from vellum.workflows.utils.uuids import uuid4_from_hash
74
+ from vellum.workflows.workflows.event_filters import workflow_event_filter
82
75
 
83
76
 
84
77
  class _BaseWorkflowMeta(type):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.12.13
3
+ Version: 0.12.14
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -72,12 +72,14 @@ vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86Fde
72
72
  vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=ydAbFMzcY2LURINZbXYm9BAXZdIa3-7rQ86Kupo7qcA,12804
73
73
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=AMxNnTm2z3LIR5rqxoCAfuy37F2FTuSRDVtKUoezO8M,1184
74
74
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=GhIviEMDWNw1p8z20ta08T5PeNCVJs5p2hrOX1uyNxg,17066
75
- vellum/__init__.py,sha256=FTKuzi4UX1MAYpZnW-JbtU8WgOzUIfolgajVVR96LGs,35552
75
+ vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
+ vellum_ee/workflows/server/virtual_file_loader.py,sha256=sQQFqn7xI0GfUlypWkVFl-X5ShXTh9bXq8N6qlQvSoE,1452
77
+ vellum/__init__.py,sha256=eS4rbj1wrussRENYQRA1GZ5JgcbIlqDad_ARuOihIYE,35624
76
78
  vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
77
79
  vellum/client/__init__.py,sha256=z59nOGe27vMDqsU-ljfULBwC5J4nyrqFunhmo8xnxbU,111521
78
80
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
79
81
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
80
- vellum/client/core/client_wrapper.py,sha256=vjueLRJ8AxJ_TUVQkuXwAW-kVfBfyveLY3BAgl-dZqE,1869
82
+ vellum/client/core/client_wrapper.py,sha256=eCp0imbOxsKZE981B9ikNz1dw1PL356Io3515uO93DY,1869
81
83
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
82
84
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
83
85
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -656,7 +658,7 @@ vellum/evaluations/utils/env.py,sha256=Xj_nxsoU5ox06EOTjRopR4lrigQI6Le6qbWGltYoE
656
658
  vellum/evaluations/utils/exceptions.py,sha256=dXMAkzqbHV_AP5FjjbegPlfUE0zQDlpA3qOsoOJUxfg,49
657
659
  vellum/evaluations/utils/paginator.py,sha256=rEED_BJAXAM6tM1yMwHePNzszjq_tTq4NbQvi1jWQ_Q,697
658
660
  vellum/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
659
- vellum/plugins/pydantic.py,sha256=EbI0pJMhUS9rLPSkzmAELfnCHrWCJzOrU06T8ommwdw,2334
661
+ vellum/plugins/pydantic.py,sha256=dNtZWHo-IdseG52C2RoTanxyTJg0AhPZrH-9lbNqwYg,2604
660
662
  vellum/plugins/utils.py,sha256=U9ZY9KdE3RRvbcG01hXxu9CvfJD6Fo7nJDgcHjQn0FI,606
661
663
  vellum/plugins/vellum_mypy.py,sha256=VC15EzjTsXOb9uF1bky4rcxePP-0epMVmCsLB2z4Dh8,24816
662
664
  vellum/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1218,7 +1220,7 @@ vellum/workflows/constants.py,sha256=Z0W4YlqfSlSgWC11PrVUPs6ZOBeIaQ78E_90J1hohiw
1218
1220
  vellum/workflows/context.py,sha256=R8qdsFbD_0p7B6PWnyvSrZ_aOgMtGw-_uk0P0UAmwLA,1230
1219
1221
  vellum/workflows/descriptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1220
1222
  vellum/workflows/descriptors/base.py,sha256=BhYd5O9_3fjS_Vet9Q2_kyUJCySHGVM_HWaOBtctkNA,14320
1221
- vellum/workflows/descriptors/tests/test_utils.py,sha256=bjWNlmyCmtpDZmIzwYmS1Yh7kulmMO6LgNJFYVFCg4o,4377
1223
+ vellum/workflows/descriptors/tests/test_utils.py,sha256=1CFx6Yejrg_AlwSO6kqGjuav4-ZubCLN0u4NtR3KgCk,4510
1222
1224
  vellum/workflows/descriptors/utils.py,sha256=lO_dbr5g3PXpHPtVBkdguAK4-1qayZ7RXjl3BgAhrMM,3795
1223
1225
  vellum/workflows/edges/__init__.py,sha256=wSkmAnz9xyi4vZwtDbKxwlplt2skD7n3NsxkvR_pUus,50
1224
1226
  vellum/workflows/edges/edge.py,sha256=N0SnY3gKVuxImPAdCbPMPlHJIXbkQ3fwq_LbJRvVMFc,677
@@ -1273,29 +1275,30 @@ vellum/workflows/inputs/base.py,sha256=1kMgr0WqCYdWUqgFvgSoAMw2067FAlgwhGXLgbIOr
1273
1275
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1274
1276
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1275
1277
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1276
- vellum/workflows/nodes/bases/base.py,sha256=_Ms_Yov6sIsgWnKb0OWnOG7liqgjFSat0y1OIX36hFA,14235
1278
+ vellum/workflows/nodes/bases/base.py,sha256=RREFzYPxemKUvQc0NfnwQmby-p_BE3O-TbVWKbQFdfs,14271
1279
+ vellum/workflows/nodes/bases/base_adornment_node.py,sha256=eFTgsPCYb3eyGS0-kw7C6crFnwFx437R5wh9-8bWYts,2905
1277
1280
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1278
- vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=ghpLm_ljNBxWs43GlvIId47hOVwKU50XpvZxmlg6_8g,3439
1281
+ vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=51CueFVty9XYASC0rKr1cXWejho5WElmhfhp6cCONy0,3811
1279
1282
  vellum/workflows/nodes/core/__init__.py,sha256=5zDMCmyt1v0HTJzlUBwq3U9L825yZGZhT9JL18-mRR4,455
1280
1283
  vellum/workflows/nodes/core/error_node/__init__.py,sha256=g7RRnlHhqu4qByfLjBwCunmgGA8dI5gNsjS3h6TwlSI,60
1281
1284
  vellum/workflows/nodes/core/error_node/node.py,sha256=MFHU5vITYSK-L9CuMZ49In2ZeNLWnhZD0f8r5dWvb5Y,1270
1282
1285
  vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py,sha256=nKNEH1QTl-1PcvmYoqSWEl0-t6gAur8GLTXHzklRQfM,84
1283
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=btscTP3T5VzvuNpbzaUlRZ1ahMkTox0Vyv17tI7YFUw,3467
1286
+ vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=zyKvpeNoD2D7vtGbNuvqXUxVgtkmSbk1XQExwX9KUf8,3602
1284
1287
  vellum/workflows/nodes/core/inline_subworkflow_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1285
1288
  vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py,sha256=99LLPgHMqohGL-G4KqOSjeVlDR_FH8Rv1gtRii85MC4,1143
1286
1289
  vellum/workflows/nodes/core/map_node/__init__.py,sha256=MXpZYmGfhsMJHqqlpd64WiJRtbAtAMQz-_3fCU_cLV0,56
1287
- vellum/workflows/nodes/core/map_node/node.py,sha256=5Bqi0co1T9Ex_sTlyTDMGahwsUWd9NCO0vTY6UqsZeQ,7286
1290
+ vellum/workflows/nodes/core/map_node/node.py,sha256=DTMoGqtR8MyfZ8jy8apNoN-4KFFFHywo87pfpqyBVEw,7322
1288
1291
  vellum/workflows/nodes/core/map_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1289
1292
  vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=RHSZs7t6mW3UWvRrXnHZqaXVdRT2ZquOK_YHJ-gzXsU,1871
1290
1293
  vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
1291
- vellum/workflows/nodes/core/retry_node/node.py,sha256=IjNcpzFmHyBUjOHEoULLbKf85B5Dva2XNR6jxPxZeoc,4223
1294
+ vellum/workflows/nodes/core/retry_node/node.py,sha256=lAABgo2E_pWkzOYUBGzC1SnywgwtGuJojwT602fKCUc,3153
1292
1295
  vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1293
1296
  vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=QXTnHwmJHISxXjvZMeuuEo0iVugVMJyaJoggI8yKXfI,3132
1294
1297
  vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
1295
1298
  vellum/workflows/nodes/core/templating_node/node.py,sha256=N-NOBd-UY91qO9orCcW4KEbhNvDQivZPA-PCxs-M0RM,4204
1296
1299
  vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=nW_kyJ9RAqz45_kJE_rlhOOvbV4OO3hecP-P-ydQpkw,2845
1297
1300
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1298
- vellum/workflows/nodes/core/try_node/node.py,sha256=e4gavIJ9AHM0JCKOrBIeKC4aHxmJK3j17XTBaipqfpc,6637
1301
+ vellum/workflows/nodes/core/try_node/node.py,sha256=_lTmSYCiz7lktaxpNWUCglNi8_5Sfy8Rpiov5SeKVMw,3920
1299
1302
  vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1300
1303
  vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=Wc2kLl-MkffsBxl3IiFaqLd16e2Iosxhk7qBnojPvQg,4092
1301
1304
  vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDXl0Nmz4QzJV2PYe5RI,1023
@@ -1346,7 +1349,7 @@ vellum/workflows/nodes/experimental/README.md,sha256=eF6DfIL8t-HbF9-mcofOMymKrra
1346
1349
  vellum/workflows/nodes/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1347
1350
  vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
1348
1351
  vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=1EGeiaT-Zoo6pttQFKKBcdf3dmhAbjKGaErYD5FFwlc,10185
1349
- vellum/workflows/nodes/utils.py,sha256=EZt7CzJmgQBR_GWFpZr8d-oaoti3tolTd2Cv9wm7dKo,1087
1352
+ vellum/workflows/nodes/utils.py,sha256=chSsmKe_BsvMIJpzSxO5TWYlr3sAuxiwkfB5azkuN5Q,2715
1350
1353
  vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
1351
1354
  vellum/workflows/outputs/base.py,sha256=a7W6rNSDSawwGAXYjNTF2iHb9lnZu7WFSOagZIyy__k,7976
1352
1355
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
@@ -1354,6 +1357,7 @@ vellum/workflows/ports/node_ports.py,sha256=g4A-8iUAvEJSkaWppbvzAR8XU02R9U-qLN4r
1354
1357
  vellum/workflows/ports/port.py,sha256=rc3GB7dDQCUs0IbY08a92-31YzJHQgBeww13brSJ2Js,3172
1355
1358
  vellum/workflows/ports/utils.py,sha256=pEjVNJKw9LhD_cFN-o0MWBOW2ejno7jv26qqzjLxwS4,1662
1356
1359
  vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcvEcsUBrzLIs,591
1360
+ vellum/workflows/references/constant.py,sha256=6yUT4q1sMj1hkI_tzzQ9AYcmeeDYFUNCqUq_W2DN0S8,540
1357
1361
  vellum/workflows/references/environment_variable.py,sha256=7FFtiKfc4eyVkkfUbhc666OBNDqvFlMoNQEYmGpEVVE,661
1358
1362
  vellum/workflows/references/execution_count.py,sha256=JILHqt8ELdc9ct-WsVCA5X-rKiP1rmJODw-XTf4kpHI,722
1359
1363
  vellum/workflows/references/external_input.py,sha256=XHugauKYvAmsGoFnjgJh00FcXjSMIqBvRun_CZuJD64,1662
@@ -1367,7 +1371,7 @@ vellum/workflows/references/workflow_input.py,sha256=86IuhlBz-9cGxeUzizyjdp482aj
1367
1371
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1368
1372
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1369
1373
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1370
- vellum/workflows/runner/runner.py,sha256=DuFvMyejwdIdapQDHrfVtouaifLEZ02WYPARmK3UqdE,28006
1374
+ vellum/workflows/runner/runner.py,sha256=wS9GulRM8f77sQ9-FlDpHjQJgoMLt0cTDRSTpucTyr0,28139
1371
1375
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1372
1376
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1373
1377
  vellum/workflows/state/base.py,sha256=jpSzF1OQd3-fqi6dMGlNsQl-7JnJxCdzWIigmX8Wz-I,14425
@@ -1380,6 +1384,7 @@ vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
1380
1384
  vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVik6ZVTfVOA,1826
1381
1385
  vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
1382
1386
  vellum/workflows/types/core.py,sha256=D2NcSBwGgWj_mtXZqe3KnEQcb5qd5HzqAwnxwmlCfCw,899
1387
+ vellum/workflows/types/cycle_map.py,sha256=-ZMQsKzZBpCi0bchJrkuN_dtCFuz9uFABy7Fq2PI58E,928
1383
1388
  vellum/workflows/types/generics.py,sha256=ZkfoRhWs042i5IjA99v2wIhmh1u-Wieo3LzosgGWJVk,600
1384
1389
  vellum/workflows/types/stack.py,sha256=RDSGLkcV612ge8UuAH9TZiEGXxJt0Av2-H5rfzrTVVI,1014
1385
1390
  vellum/workflows/types/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1397,10 +1402,10 @@ vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrg
1397
1402
  vellum/workflows/utils/vellum_variables.py,sha256=g5xHYB8etfHE32ek19nP6Anf8NyjhmUtOwO2KmQ5xZU,3111
1398
1403
  vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528t75s,683
1399
1404
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1400
- vellum/workflows/workflows/base.py,sha256=TLk5NiDB8N_Ytkzj_T8o-E4WpTUH8qJYZcoOk2xR3mo,18986
1405
+ vellum/workflows/workflows/base.py,sha256=qdZYQq-jjdr0fYT0FCfmFuI5ypE3pANupgYcOqqML0o,18884
1401
1406
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1402
- vellum_ai-0.12.13.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1403
- vellum_ai-0.12.13.dist-info/METADATA,sha256=uXAzy9xHbkmecOGR1GasDk6auS-Hbl12VwdAgqAuo5c,5161
1404
- vellum_ai-0.12.13.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1405
- vellum_ai-0.12.13.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1406
- vellum_ai-0.12.13.dist-info/RECORD,,
1407
+ vellum_ai-0.12.14.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1408
+ vellum_ai-0.12.14.dist-info/METADATA,sha256=pMVsOWid4kf15trT9SP0w9_wfPpc-E6i4dV8rsUAfSc,5161
1409
+ vellum_ai-0.12.14.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1410
+ vellum_ai-0.12.14.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1411
+ vellum_ai-0.12.14.dist-info/RECORD,,
File without changes
@@ -0,0 +1,42 @@
1
+ import importlib
2
+
3
+
4
+ class VirtualFileLoader(importlib.abc.Loader):
5
+ def __init__(self, code: str, is_package: bool):
6
+ self.code = code
7
+ self.is_package = is_package
8
+
9
+ def create_module(self, spec):
10
+ return None # use default module creation
11
+
12
+ def exec_module(self, module):
13
+ if not self.is_package:
14
+ exec(self.code, module.__dict__)
15
+
16
+
17
+ class VirtualFileFinder(importlib.abc.MetaPathFinder, importlib.abc.Loader):
18
+ def __init__(self, files: dict[str, str], namespace: str):
19
+ self.files = files
20
+ self.namespace = namespace
21
+
22
+ def find_spec(self, fullname, path, target=None):
23
+ # Do the namespacing on the fly to avoid having to copy the file dict
24
+ prefixed_name = fullname if fullname.startswith(self.namespace) else f"{self.namespace}.{fullname}"
25
+
26
+ key_name = "__init__" if fullname == self.namespace else fullname.replace(f"{self.namespace}.", "")
27
+
28
+ files_key = f"{key_name.replace('.', '/')}.py"
29
+ if not self.files.get(files_key):
30
+ files_key = f"{key_name.replace('.', '/')}/__init__.py"
31
+
32
+ file = self.files.get(files_key)
33
+ is_package = "__init__" in files_key
34
+
35
+ if file:
36
+ return importlib.machinery.ModuleSpec(
37
+ prefixed_name,
38
+ VirtualFileLoader(file, is_package),
39
+ origin=prefixed_name,
40
+ is_package=is_package,
41
+ )
42
+ return None