vellum-ai 0.14.15__py3-none-any.whl → 0.14.17__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 (40) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/resources/document_indexes/client.py +0 -55
  4. vellum/client/types/__init__.py +2 -0
  5. vellum/client/types/document_index_read.py +0 -10
  6. vellum/client/types/release.py +21 -0
  7. vellum/client/types/workflow_release_tag_read.py +7 -1
  8. vellum/plugins/pydantic.py +14 -4
  9. vellum/prompts/blocks/compilation.py +14 -0
  10. vellum/types/release.py +3 -0
  11. vellum/workflows/nodes/bases/base.py +7 -7
  12. vellum/workflows/nodes/bases/base_adornment_node.py +2 -0
  13. vellum/workflows/nodes/core/retry_node/node.py +1 -1
  14. vellum/workflows/nodes/core/try_node/node.py +1 -1
  15. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +4 -0
  16. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +27 -1
  17. vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py +0 -0
  18. vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +182 -0
  19. vellum/workflows/nodes/displayable/inline_prompt_node/node.py +4 -1
  20. vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py +7 -1
  21. vellum/workflows/utils/tests/test_vellum_variables.py +7 -1
  22. vellum/workflows/utils/vellum_variables.py +4 -0
  23. vellum/workflows/vellum_client.py +9 -5
  24. {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/METADATA +1 -1
  25. {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/RECORD +40 -35
  26. vellum_cli/image_push.py +76 -42
  27. vellum_cli/tests/test_image_push.py +56 -0
  28. vellum_ee/workflows/display/nodes/base_node_display.py +35 -29
  29. vellum_ee/workflows/display/nodes/get_node_display_class.py +0 -9
  30. vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +38 -18
  31. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -0
  32. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +29 -33
  33. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +91 -106
  34. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +33 -38
  35. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +138 -153
  36. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +23 -26
  37. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +51 -7
  38. {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/LICENSE +0 -0
  39. {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/WHEEL +0 -0
  40. {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/entry_points.txt +0 -0
@@ -59,7 +59,16 @@ _NodeDisplayAttrType = TypeVar("_NodeDisplayAttrType")
59
59
 
60
60
  class BaseNodeDisplayMeta(type):
61
61
  def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
62
- cls = super().__new__(mcs, name, bases, dct)
62
+ cls = cast(Type["BaseNodeDisplay"], super().__new__(mcs, name, bases, dct))
63
+
64
+ if not dct.get("output_display"):
65
+ node_class = cls.infer_node_class()
66
+ cls.output_display = {
67
+ ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
68
+ for ref in node_class.Outputs
69
+ if ref.name in node_class.__output_ids__
70
+ }
71
+
63
72
  return cls.__annotate_node__()
64
73
 
65
74
  def __annotate_node__(cls):
@@ -73,22 +82,36 @@ class BaseNodeDisplayMeta(type):
73
82
  # Display classes are able to override the id of the node class it's parameterized by
74
83
  node_class.__id__ = display_node_id
75
84
 
76
- output_display = getattr(cls, "output_display", None)
77
- if isinstance(output_display, dict):
78
- # And the node class' output ids
79
- for reference, node_output_display in output_display.items():
80
- if not isinstance(reference, OutputReference):
81
- continue
82
- if not isinstance(node_output_display, NodeOutputDisplay):
83
- continue
85
+ for reference, node_output_display in base_node_display_class.output_display.items():
86
+ if not isinstance(reference, OutputReference):
87
+ continue
88
+ if not isinstance(node_output_display, NodeOutputDisplay):
89
+ continue
84
90
 
85
- node_class.__output_ids__[reference.name] = node_output_display.id
91
+ node_class.__output_ids__[reference.name] = node_output_display.id
86
92
 
87
93
  return cls
88
94
 
95
+ def infer_node_class(cls) -> Type[BaseNode]:
96
+ original_base = get_original_base(cls)
97
+ node_class = get_args(original_base)[0]
98
+ if isinstance(node_class, TypeVar):
99
+ bounded_class = node_class.__bound__
100
+ if inspect.isclass(bounded_class) and issubclass(bounded_class, BaseNode):
101
+ return bounded_class
102
+
103
+ if isinstance(bounded_class, ForwardRef) and bounded_class.__forward_arg__ == BaseNode.__name__:
104
+ return BaseNode
105
+
106
+ if issubclass(node_class, BaseNode):
107
+ return node_class
108
+
109
+ raise ValueError(f"Node {cls.__name__} must be a subclass of {BaseNode.__name__}")
110
+
89
111
 
90
112
  class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
91
- output_display: Dict[OutputReference, NodeOutputDisplay] = {}
113
+ # Default values set by the metaclass
114
+ output_display: Dict[OutputReference, NodeOutputDisplay]
92
115
  port_displays: Dict[Port, PortDisplayOverrides] = {}
93
116
  node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
94
117
 
@@ -235,23 +258,6 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
235
258
  def get_from_node_display_registry(cls, node_class: Type[NodeType]) -> Optional[Type["BaseNodeDisplay"]]:
236
259
  return cls._node_display_registry.get(node_class)
237
260
 
238
- @classmethod
239
- def infer_node_class(cls) -> Type[NodeType]:
240
- original_base = get_original_base(cls)
241
- node_class = get_args(original_base)[0]
242
- if isinstance(node_class, TypeVar):
243
- bounded_class = node_class.__bound__
244
- if inspect.isclass(bounded_class) and issubclass(bounded_class, BaseNode):
245
- return cast(Type[NodeType], bounded_class)
246
-
247
- if isinstance(bounded_class, ForwardRef) and bounded_class.__forward_arg__ == BaseNode.__name__:
248
- return cast(Type[NodeType], BaseNode)
249
-
250
- if issubclass(node_class, BaseNode):
251
- return node_class
252
-
253
- raise ValueError(f"Node {cls.__name__} must be a subclass of {BaseNode.__name__}")
254
-
255
261
  @cached_property
256
262
  def node_id(self) -> UUID:
257
263
  """Can be overridden as a class attribute to specify a custom node id."""
@@ -264,7 +270,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
264
270
 
265
271
  @property
266
272
  def _node(self) -> Type[NodeType]:
267
- return self.infer_node_class()
273
+ return cast(Type[NodeType], self.__class__.infer_node_class())
268
274
 
269
275
  @classmethod
270
276
  def _get_explicit_node_display_attr(
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any, Dict, Optional, Type
5
5
  from vellum.workflows.descriptors.base import BaseDescriptor
6
6
  from vellum.workflows.types.generics import NodeType
7
7
  from vellum.workflows.utils.uuids import uuid4_from_hash
8
- from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
9
8
 
10
9
  if TYPE_CHECKING:
11
10
  from vellum_ee.workflows.display.types import NodeDisplayType
@@ -43,14 +42,6 @@ def get_node_display_class(
43
42
  return {}
44
43
 
45
44
  def exec_body(ns: Dict):
46
- output_display = {
47
- ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
48
- for ref in node_class.Outputs
49
- if ref.name in node_class.__output_ids__
50
- }
51
- if output_display:
52
- ns["output_display"] = output_display
53
-
54
45
  node_input_ids_by_name: Dict[str, UUID] = {}
55
46
  for ref in node_class:
56
47
  node_input_ids_by_name.update(_get_node_input_ids_by_ref(ref.name, ref.instance))
@@ -3,7 +3,6 @@ import types
3
3
  from uuid import UUID
4
4
  from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar, cast
5
5
 
6
- from vellum.workflows.nodes.bases.base import BaseNode
7
6
  from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
8
7
  from vellum.workflows.nodes.utils import get_wrapped_node
9
8
  from vellum.workflows.types.core import JsonArray, JsonObject
@@ -12,6 +11,7 @@ from vellum.workflows.utils.uuids import uuid4_from_hash
12
11
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
13
12
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
14
13
  from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
14
+ from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
15
15
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
16
16
 
17
17
  _BaseAdornmentNodeType = TypeVar("_BaseAdornmentNodeType", bound=BaseAdornmentNode)
@@ -44,12 +44,17 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
44
44
  return serialized_wrapped_node
45
45
 
46
46
  @classmethod
47
- def wrap(cls, **kwargs: Any) -> Callable[..., Type[BaseNodeDisplay]]:
47
+ def wrap(cls, node_id: Optional[UUID] = None, **kwargs: Any) -> Callable[..., Type[BaseNodeDisplay]]:
48
48
  NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
49
49
 
50
- def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
51
- node_class = inner_cls.infer_node_class()
52
- wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
50
+ def decorator(wrapped_node_display_class: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
51
+ node_class = wrapped_node_display_class.infer_node_class()
52
+ if not issubclass(node_class, BaseAdornmentNode):
53
+ raise ValueError(f"Node {node_class.__name__} must be wrapped with a {BaseAdornmentNode.__name__}")
54
+
55
+ wrapped_node_class = node_class.__wrapped_node__
56
+ if not wrapped_node_class:
57
+ raise ValueError(f"Node {node_class.__name__} must be used as an adornment with the `wrap` method.")
53
58
 
54
59
  # `mypy` is wrong here, `cls` is indexable bc it's Generic
55
60
  BaseAdornmentDisplay = cls[node_class] # type: ignore[index]
@@ -58,36 +63,51 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
58
63
  for key, kwarg in kwargs.items():
59
64
  ns[key] = kwarg
60
65
 
61
- if "node_id" not in kwargs:
62
- ns["node_id"] = uuid4_from_hash(node_class.__qualname__)
66
+ ns["node_id"] = node_id or uuid4_from_hash(node_class.__qualname__)
63
67
 
64
68
  AdornmentDisplay = types.new_class(
65
69
  re.sub(r"^Base", "", cls.__name__), bases=(BaseAdornmentDisplay,), exec_body=exec_body
66
70
  )
67
71
 
68
- setattr(inner_cls, "__adorned_by__", AdornmentDisplay)
72
+ setattr(wrapped_node_display_class, "__adorned_by__", AdornmentDisplay)
69
73
 
70
74
  # We must edit the node display class to use __wrapped_node__ everywhere it
71
75
  # references the adorned node class, which is three places:
72
76
 
73
77
  # 1. The node display class' parameterized type
74
- original_base_node_display = get_original_base(inner_cls)
78
+ original_base_node_display = get_original_base(wrapped_node_display_class)
75
79
  original_base_node_display.__args__ = (wrapped_node_class,)
76
- inner_cls._node_display_registry[wrapped_node_class] = inner_cls
77
- inner_cls.__annotate_node__()
80
+ wrapped_node_display_class._node_display_registry[wrapped_node_class] = wrapped_node_display_class
81
+ wrapped_node_display_class.__annotate_node__()
78
82
 
79
83
  # 2. The node display class' output displays
80
- old_outputs = list(inner_cls.output_display.keys())
81
- for old_output in old_outputs:
82
- new_output = getattr(wrapped_node_class.Outputs, old_output.name)
83
- inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
84
+ for old_output in node_class.Outputs:
85
+ new_output = getattr(wrapped_node_class.Outputs, old_output.name, None)
86
+ if new_output is None:
87
+ # If the adornment is adding a new output, such as TryNode adding an "error" output,
88
+ # we skip it, since it should not be included in the adorned node's output displays
89
+ wrapped_node_display_class.output_display.pop(old_output, None)
90
+ continue
91
+
92
+ if old_output not in wrapped_node_display_class.output_display:
93
+ # If the adorned node doesn't have an output display defined for this output, we define one
94
+ wrapped_node_display_class.output_display[new_output] = NodeOutputDisplay(
95
+ id=wrapped_node_class.__output_ids__[old_output.name],
96
+ name=old_output.name,
97
+ )
98
+ else:
99
+ wrapped_node_display_class.output_display[new_output] = (
100
+ wrapped_node_display_class.output_display.pop(old_output)
101
+ )
84
102
 
85
103
  # 3. The node display class' port displays
86
- old_ports = list(inner_cls.port_displays.keys())
104
+ old_ports = list(wrapped_node_display_class.port_displays.keys())
87
105
  for old_port in old_ports:
88
106
  new_port = getattr(wrapped_node_class.Ports, old_port.name)
89
- inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
107
+ wrapped_node_display_class.port_displays[new_port] = wrapped_node_display_class.port_displays.pop(
108
+ old_port
109
+ )
90
110
 
91
- return inner_cls
111
+ return wrapped_node_display_class
92
112
 
93
113
  return decorator
@@ -19,6 +19,7 @@ _InlinePromptNodeType = TypeVar("_InlinePromptNodeType", bound=InlinePromptNode)
19
19
  class BaseInlinePromptNodeDisplay(BaseNodeVellumDisplay[_InlinePromptNodeType], Generic[_InlinePromptNodeType]):
20
20
  output_id: ClassVar[Optional[UUID]] = None
21
21
  array_output_id: ClassVar[Optional[UUID]] = None
22
+ json_output_id: ClassVar[Optional[UUID]] = None
22
23
  prompt_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
23
24
 
24
25
  def serialize(
@@ -21,20 +21,18 @@ class Inputs(BaseInputs):
21
21
  input: str
22
22
 
23
23
 
24
- @RetryNode.wrap(max_attempts=3)
25
- class InnerRetryGenericNode(BaseNode):
26
- input = Inputs.input
27
-
28
- class Outputs(BaseOutputs):
29
- output: str
30
-
24
+ def test_serialize_node__retry(serialize_node):
25
+ @RetryNode.wrap(max_attempts=3)
26
+ class InnerRetryGenericNode(BaseNode):
27
+ input = Inputs.input
31
28
 
32
- @BaseRetryNodeDisplay.wrap(max_attempts=3)
33
- class InnerRetryGenericNodeDisplay(BaseNodeDisplay[InnerRetryGenericNode]):
34
- pass
29
+ class Outputs(BaseOutputs):
30
+ output: str
35
31
 
32
+ @BaseRetryNodeDisplay.wrap(max_attempts=3)
33
+ class InnerRetryGenericNodeDisplay(BaseNodeDisplay[InnerRetryGenericNode]):
34
+ pass
36
35
 
37
- def test_serialize_node__retry(serialize_node):
38
36
  input_id = uuid4()
39
37
  serialized_node = serialize_node(
40
38
  node_class=InnerRetryGenericNode,
@@ -49,8 +47,8 @@ def test_serialize_node__retry(serialize_node):
49
47
  )
50
48
  assert not DeepDiff(
51
49
  {
52
- "id": "f2a95e79-7d4b-47ad-b986-4f648297ec65",
53
- "label": "InnerRetryGenericNode",
50
+ "id": "188b50aa-e518-4b7b-a5e0-e2585fb1d7b5",
51
+ "label": "test_serialize_node__retry.<locals>.InnerRetryGenericNode",
54
52
  "type": "GENERIC",
55
53
  "display_data": {"position": {"x": 0.0, "y": 0.0}},
56
54
  "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
@@ -66,8 +64,8 @@ def test_serialize_node__retry(serialize_node):
66
64
  "test_adornments_serialization",
67
65
  ],
68
66
  },
69
- "trigger": {"id": "af9ba01c-4cde-4632-9aa1-7673b42e7bd8", "merge_behavior": "AWAIT_ATTRIBUTES"},
70
- "ports": [{"id": "c2ecc6c0-f353-4495-9b93-a61a47248556", "name": "default", "type": "DEFAULT"}],
67
+ "trigger": {"id": "d38a83bf-23d1-4f9d-a875-a08dc27cf397", "merge_behavior": "AWAIT_ATTRIBUTES"},
68
+ "ports": [{"id": "078650c9-f775-4cd0-a08c-23af9983a361", "name": "default", "type": "DEFAULT"}],
71
69
  "adornments": [
72
70
  {
73
71
  "id": "5be7d260-74f7-4734-b31b-a46a94539586",
@@ -102,13 +100,13 @@ def test_serialize_node__retry(serialize_node):
102
100
  ],
103
101
  "attributes": [
104
102
  {
105
- "id": "c363daa7-9482-4c0e-aee8-faa080602ee3",
103
+ "id": "278df25e-58b5-43c3-b346-cf6444d893a5",
106
104
  "name": "input",
107
105
  "value": {"type": "WORKFLOW_INPUT", "input_variable_id": str(input_id)},
108
106
  }
109
107
  ],
110
108
  "outputs": [
111
- {"id": "8aaf6cd8-3fa5-4f17-a60f-ec7da5ec6498", "name": "output", "type": "STRING", "value": None}
109
+ {"id": "dc89dc0d-c0bd-47fd-88aa-ec7b262aa2f1", "name": "output", "type": "STRING", "value": None}
112
110
  ],
113
111
  },
114
112
  serialized_node,
@@ -136,20 +134,18 @@ def test_serialize_node__retry__no_display():
136
134
  assert exec_config is not None
137
135
 
138
136
 
139
- @TryNode.wrap()
140
- class InnerTryGenericNode(BaseNode):
141
- input = Inputs.input
142
-
143
- class Outputs(BaseOutputs):
144
- output: str
145
-
137
+ def test_serialize_node__try(serialize_node):
138
+ @TryNode.wrap()
139
+ class InnerTryGenericNode(BaseNode):
140
+ input = Inputs.input
146
141
 
147
- @BaseTryNodeDisplay.wrap()
148
- class InnerTryGenericNodeDisplay(BaseNodeDisplay[InnerTryGenericNode]):
149
- pass
142
+ class Outputs(BaseOutputs):
143
+ output: str
150
144
 
145
+ @BaseTryNodeDisplay.wrap()
146
+ class InnerTryGenericNodeDisplay(BaseNodeDisplay[InnerTryGenericNode]):
147
+ pass
151
148
 
152
- def test_serialize_node__try(serialize_node):
153
149
  input_id = uuid4()
154
150
  serialized_node = serialize_node(
155
151
  base_class=BaseNodeVellumDisplay,
@@ -163,7 +159,7 @@ def test_serialize_node__try(serialize_node):
163
159
  assert not DeepDiff(
164
160
  {
165
161
  "id": str(InnerTryGenericNode.__wrapped_node__.__id__),
166
- "label": "InnerTryGenericNode",
162
+ "label": "test_serialize_node__try.<locals>.InnerTryGenericNode",
167
163
  "type": "GENERIC",
168
164
  "display_data": {"position": {"x": 0.0, "y": 0.0}},
169
165
  "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
@@ -179,8 +175,8 @@ def test_serialize_node__try(serialize_node):
179
175
  "test_adornments_serialization",
180
176
  ],
181
177
  },
182
- "trigger": {"id": "741f7f75-e921-47a9-8c05-9e66640d0866", "merge_behavior": "AWAIT_ATTRIBUTES"},
183
- "ports": [{"id": "1b8f8ab5-a656-4015-926c-80655bbd9cb8", "name": "default", "type": "DEFAULT"}],
178
+ "trigger": {"id": "16bc1522-c408-47ad-9a22-0ef136384abf", "merge_behavior": "AWAIT_ATTRIBUTES"},
179
+ "ports": [{"id": "8d25f244-4b12-4f8b-b202-8948698679a0", "name": "default", "type": "DEFAULT"}],
184
180
  "adornments": [
185
181
  {
186
182
  "id": "3344083c-a32c-4a32-920b-0fb5093448fa",
@@ -200,13 +196,13 @@ def test_serialize_node__try(serialize_node):
200
196
  ],
201
197
  "attributes": [
202
198
  {
203
- "id": "4d8b4c2c-4f92-4c7a-abf0-b9c88a15a790",
199
+ "id": "51aa0077-4060-496b-8e2e-e79d56ee6a32",
204
200
  "name": "input",
205
201
  "value": {"type": "WORKFLOW_INPUT", "input_variable_id": str(input_id)},
206
202
  }
207
203
  ],
208
204
  "outputs": [
209
- {"id": "63ba929b-bf79-44ee-bd1f-d259dbe8d48e", "name": "output", "type": "STRING", "value": None}
205
+ {"id": "ce9f8b86-6d26-4c03-8bfa-a31aa2cd97f1", "name": "output", "type": "STRING", "value": None}
210
206
  ],
211
207
  },
212
208
  serialized_node,