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.
- vellum/__init__.py +2 -0
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/resources/document_indexes/client.py +0 -55
- vellum/client/types/__init__.py +2 -0
- vellum/client/types/document_index_read.py +0 -10
- vellum/client/types/release.py +21 -0
- vellum/client/types/workflow_release_tag_read.py +7 -1
- vellum/plugins/pydantic.py +14 -4
- vellum/prompts/blocks/compilation.py +14 -0
- vellum/types/release.py +3 -0
- vellum/workflows/nodes/bases/base.py +7 -7
- vellum/workflows/nodes/bases/base_adornment_node.py +2 -0
- vellum/workflows/nodes/core/retry_node/node.py +1 -1
- vellum/workflows/nodes/core/try_node/node.py +1 -1
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +4 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +27 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +182 -0
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +4 -1
- vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py +7 -1
- vellum/workflows/utils/tests/test_vellum_variables.py +7 -1
- vellum/workflows/utils/vellum_variables.py +4 -0
- vellum/workflows/vellum_client.py +9 -5
- {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/RECORD +40 -35
- vellum_cli/image_push.py +76 -42
- vellum_cli/tests/test_image_push.py +56 -0
- vellum_ee/workflows/display/nodes/base_node_display.py +35 -29
- vellum_ee/workflows/display/nodes/get_node_display_class.py +0 -9
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +38 -18
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +29 -33
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +91 -106
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +33 -38
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +138 -153
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +23 -26
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +51 -7
- {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.15.dist-info → vellum_ai-0.14.17.dist-info}/WHEEL +0 -0
- {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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
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
|
-
|
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(
|
51
|
-
node_class =
|
52
|
-
|
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
|
-
|
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(
|
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(
|
78
|
+
original_base_node_display = get_original_base(wrapped_node_display_class)
|
75
79
|
original_base_node_display.__args__ = (wrapped_node_class,)
|
76
|
-
|
77
|
-
|
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
|
-
|
81
|
-
|
82
|
-
new_output
|
83
|
-
|
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(
|
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
|
-
|
107
|
+
wrapped_node_display_class.port_displays[new_port] = wrapped_node_display_class.port_displays.pop(
|
108
|
+
old_port
|
109
|
+
)
|
90
110
|
|
91
|
-
return
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
33
|
-
|
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": "
|
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": "
|
70
|
-
"ports": [{"id": "
|
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": "
|
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": "
|
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
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
148
|
-
|
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": "
|
183
|
-
"ports": [{"id": "
|
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": "
|
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": "
|
205
|
+
{"id": "ce9f8b86-6d26-4c03-8bfa-a31aa2cd97f1", "name": "output", "type": "STRING", "value": None}
|
210
206
|
],
|
211
207
|
},
|
212
208
|
serialized_node,
|