vellum-ai 0.14.13__py3-none-any.whl → 0.14.15__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/client/core/client_wrapper.py +1 -1
- vellum/client/core/pydantic_utilities.py +4 -5
- vellum/client/types/logical_operator.py +1 -0
- vellum/plugins/utils.py +11 -3
- vellum/workflows/events/node.py +5 -0
- vellum/workflows/events/tests/test_event.py +36 -0
- vellum/workflows/nodes/core/retry_node/tests/test_node.py +23 -0
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +16 -0
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +29 -0
- vellum/workflows/nodes/displayable/bases/api_node/node.py +2 -2
- vellum/workflows/nodes/tests/test_utils.py +133 -0
- vellum/workflows/nodes/utils.py +17 -3
- vellum/workflows/runner/runner.py +2 -1
- {vellum_ai-0.14.13.dist-info → vellum_ai-0.14.15.dist-info}/METADATA +2 -2
- {vellum_ai-0.14.13.dist-info → vellum_ai-0.14.15.dist-info}/RECORD +30 -29
- vellum_ee/workflows/display/nodes/base_node_display.py +8 -5
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +1 -4
- vellum_ee/workflows/display/nodes/get_node_display_class.py +34 -8
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +55 -1
- vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -54
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -8
- vellum_ee/workflows/display/nodes/vellum/try_node.py +1 -42
- vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +47 -10
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +67 -0
- vellum_ee/workflows/display/utils/vellum.py +3 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +3 -6
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +52 -0
- {vellum_ai-0.14.13.dist-info → vellum_ai-0.14.15.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.13.dist-info → vellum_ai-0.14.15.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.13.dist-info → vellum_ai-0.14.15.dist-info}/entry_points.txt +0 -0
@@ -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.
|
21
|
+
"X-Fern-SDK-Version": "0.14.15",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -133,12 +133,11 @@ class UniversalBaseModel(pydantic.BaseModel):
|
|
133
133
|
#
|
134
134
|
# We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models
|
135
135
|
# that we have less control over, and this is less intrusive than custom serializers for now.
|
136
|
-
kwargs = {
|
137
|
-
**kwargs,
|
138
|
-
"warnings": False,
|
139
|
-
}
|
140
|
-
|
141
136
|
if IS_PYDANTIC_V2:
|
137
|
+
kwargs = {
|
138
|
+
**kwargs,
|
139
|
+
"warnings": False,
|
140
|
+
}
|
142
141
|
kwargs_with_defaults_exclude_unset: typing.Any = {
|
143
142
|
**kwargs,
|
144
143
|
"by_alias": True,
|
vellum/plugins/utils.py
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
from vellum.plugins.pydantic import pydantic_plugin
|
1
|
+
import pydantic
|
4
2
|
|
3
|
+
IS_PYDANTIC_V1 = pydantic.VERSION.startswith("1.")
|
5
4
|
_loaded = False
|
6
5
|
|
7
6
|
|
8
7
|
def load_runtime_plugins() -> None:
|
8
|
+
if IS_PYDANTIC_V1:
|
9
|
+
# Pydantic plugins are only available in v2, so we defer the imports
|
10
|
+
# below until we confirm we are running a supported version of pydantic
|
11
|
+
return
|
12
|
+
|
13
|
+
from pydantic.plugin import _loader as _pydantic_plugin_loader
|
14
|
+
|
15
|
+
from vellum.plugins.pydantic import pydantic_plugin
|
16
|
+
|
9
17
|
global _loaded
|
10
18
|
if _loaded:
|
11
19
|
return
|
vellum/workflows/events/node.py
CHANGED
@@ -93,6 +93,7 @@ class NodeExecutionStreamingEvent(_BaseNodeEvent):
|
|
93
93
|
class NodeExecutionFulfilledBody(_BaseNodeExecutionBody, Generic[OutputsType]):
|
94
94
|
outputs: OutputsType
|
95
95
|
invoked_ports: InvokedPorts = None
|
96
|
+
mocked: Optional[bool] = None
|
96
97
|
|
97
98
|
@field_serializer("outputs")
|
98
99
|
def serialize_outputs(self, outputs: OutputsType, _info: Any) -> Dict[str, Any]:
|
@@ -117,6 +118,10 @@ class NodeExecutionFulfilledEvent(_BaseNodeEvent, Generic[OutputsType]):
|
|
117
118
|
def invoked_ports(self) -> InvokedPorts:
|
118
119
|
return self.body.invoked_ports
|
119
120
|
|
121
|
+
@property
|
122
|
+
def mocked(self) -> Optional[bool]:
|
123
|
+
return self.body.mocked
|
124
|
+
|
120
125
|
|
121
126
|
class NodeExecutionRejectedBody(_BaseNodeExecutionBody):
|
122
127
|
error: WorkflowError
|
@@ -328,6 +328,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
|
|
328
328
|
"name": "default",
|
329
329
|
}
|
330
330
|
],
|
331
|
+
"mocked": None,
|
331
332
|
},
|
332
333
|
"parent": None,
|
333
334
|
},
|
@@ -365,6 +366,40 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
|
|
365
366
|
"name": "default",
|
366
367
|
}
|
367
368
|
],
|
369
|
+
"mocked": None,
|
370
|
+
},
|
371
|
+
"parent": None,
|
372
|
+
},
|
373
|
+
),
|
374
|
+
(
|
375
|
+
NodeExecutionFulfilledEvent(
|
376
|
+
id=UUID("123e4567-e89b-12d3-a456-426614174000"),
|
377
|
+
timestamp=datetime(2024, 1, 1, 12, 0, 0),
|
378
|
+
trace_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
|
379
|
+
span_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
|
380
|
+
body=NodeExecutionFulfilledBody(
|
381
|
+
node_definition=MockNode,
|
382
|
+
outputs=MockNode.Outputs(
|
383
|
+
example="foo",
|
384
|
+
),
|
385
|
+
mocked=True,
|
386
|
+
),
|
387
|
+
),
|
388
|
+
{
|
389
|
+
"id": "123e4567-e89b-12d3-a456-426614174000",
|
390
|
+
"api_version": "2024-10-25",
|
391
|
+
"timestamp": "2024-01-01T12:00:00",
|
392
|
+
"trace_id": "123e4567-e89b-12d3-a456-426614174000",
|
393
|
+
"span_id": "123e4567-e89b-12d3-a456-426614174000",
|
394
|
+
"name": "node.execution.fulfilled",
|
395
|
+
"body": {
|
396
|
+
"node_definition": {
|
397
|
+
"id": mock_node_uuid,
|
398
|
+
"name": "MockNode",
|
399
|
+
"module": module_root + ["events", "tests", "test_event"],
|
400
|
+
},
|
401
|
+
"outputs": {"example": "foo"},
|
402
|
+
"mocked": True,
|
368
403
|
},
|
369
404
|
"parent": None,
|
370
405
|
},
|
@@ -379,6 +414,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
|
|
379
414
|
"node.execution.streaming",
|
380
415
|
"node.execution.fulfilled",
|
381
416
|
"fulfilled_node_with_undefined_outputs",
|
417
|
+
"mocked_node",
|
382
418
|
],
|
383
419
|
)
|
384
420
|
def test_event_serialization(event, expected_json):
|
@@ -33,6 +33,29 @@ def test_retry_node__retry_on_error_code__successfully_retried():
|
|
33
33
|
assert outputs.execution_count == 3
|
34
34
|
|
35
35
|
|
36
|
+
def test_retry_node__retry_on_error_code_all():
|
37
|
+
# GIVEN a retry node that is configured to retry on all errors
|
38
|
+
@RetryNode.wrap(max_attempts=3)
|
39
|
+
class TestNode(BaseNode):
|
40
|
+
attempt_number = RetryNode.SubworkflowInputs.attempt_number
|
41
|
+
|
42
|
+
class Outputs(BaseOutputs):
|
43
|
+
execution_count: int
|
44
|
+
|
45
|
+
def run(self) -> Outputs:
|
46
|
+
if self.attempt_number < 3:
|
47
|
+
raise NodeException(message="This will be retried", code=WorkflowErrorCode.PROVIDER_ERROR)
|
48
|
+
|
49
|
+
return self.Outputs(execution_count=self.attempt_number)
|
50
|
+
|
51
|
+
# WHEN the node is run and throws a None
|
52
|
+
node = TestNode(state=BaseState())
|
53
|
+
outputs = node.run()
|
54
|
+
|
55
|
+
# THEN the exception is retried
|
56
|
+
assert outputs.execution_count == 3
|
57
|
+
|
58
|
+
|
36
59
|
def test_retry_node__retry_on_error_code__missed():
|
37
60
|
# GIVEN a retry node that is configured to retry on PROVIDER_ERROR
|
38
61
|
@RetryNode.wrap(max_attempts=3, retry_on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
@@ -283,3 +283,19 @@ def test_templating_node__function_call_as_json():
|
|
283
283
|
# AND we can access fields directly
|
284
284
|
assert outputs.result["arguments"] == {"key": "value"}
|
285
285
|
assert outputs.result["name"] == "test_function"
|
286
|
+
|
287
|
+
|
288
|
+
def test_templating_node__empty_string_to_list():
|
289
|
+
"""Test that an empty string output with list output type casts to an empty array."""
|
290
|
+
|
291
|
+
# GIVEN a templating node that outputs an empty string but has List output type
|
292
|
+
class EmptyStringToListTemplateNode(TemplatingNode[BaseState, List[str]]):
|
293
|
+
template = """{{ "" }}"""
|
294
|
+
inputs = {}
|
295
|
+
|
296
|
+
# WHEN the node is run
|
297
|
+
node = EmptyStringToListTemplateNode()
|
298
|
+
outputs = node.run()
|
299
|
+
|
300
|
+
# THEN the output should be an empty list, not raise an exception
|
301
|
+
assert outputs.result == []
|
@@ -64,3 +64,32 @@ def test_api_node_raises_error_when_api_call_fails(vellum_client):
|
|
64
64
|
|
65
65
|
# AND the API call should have been made
|
66
66
|
assert vellum_client.execute_api.call_count == 1
|
67
|
+
|
68
|
+
|
69
|
+
def test_api_node_defaults_to_get_method(vellum_client):
|
70
|
+
# GIVEN a successful API response
|
71
|
+
vellum_client.execute_api.return_value = ExecuteApiResponse(
|
72
|
+
status_code=200,
|
73
|
+
text='{"status": 200, "data": [1, 2, 3]}',
|
74
|
+
json_={"data": [1, 2, 3]},
|
75
|
+
headers={"X-Response-Header": "bar"},
|
76
|
+
)
|
77
|
+
|
78
|
+
# AND an API node without a method specified
|
79
|
+
class SimpleAPINodeWithoutMethod(APINode):
|
80
|
+
authorization_type = AuthorizationType.BEARER_TOKEN
|
81
|
+
url = "https://api.vellum.ai"
|
82
|
+
headers = {
|
83
|
+
"X-Test-Header": "foo",
|
84
|
+
}
|
85
|
+
bearer_token_value = VellumSecret(name="secret")
|
86
|
+
|
87
|
+
node = SimpleAPINodeWithoutMethod()
|
88
|
+
|
89
|
+
# WHEN we run the node
|
90
|
+
node.run()
|
91
|
+
|
92
|
+
# THEN the API call should be made with GET method
|
93
|
+
assert vellum_client.execute_api.call_count == 1
|
94
|
+
method = vellum_client.execute_api.call_args.kwargs["method"]
|
95
|
+
assert method == APIRequestMethod.GET.value
|
@@ -29,7 +29,7 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
29
29
|
merge_behavior = MergeBehavior.AWAIT_ANY
|
30
30
|
|
31
31
|
url: str
|
32
|
-
method: APIRequestMethod
|
32
|
+
method: Optional[APIRequestMethod] = APIRequestMethod.GET
|
33
33
|
data: Optional[str] = None
|
34
34
|
json: Optional[Json] = None
|
35
35
|
headers: Optional[Dict[str, Union[str, VellumSecret]]] = None
|
@@ -45,8 +45,8 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
45
45
|
|
46
46
|
def _run(
|
47
47
|
self,
|
48
|
-
method: APIRequestMethod,
|
49
48
|
url: str,
|
49
|
+
method: Optional[APIRequestMethod] = APIRequestMethod.GET,
|
50
50
|
data: Optional[Union[str, Any]] = None,
|
51
51
|
json: Any = None,
|
52
52
|
headers: Any = None,
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import pytest
|
2
|
+
from typing import List, Union
|
3
|
+
|
4
|
+
from pydantic import BaseModel
|
5
|
+
|
6
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
7
|
+
from vellum.workflows.exceptions import NodeException
|
8
|
+
from vellum.workflows.nodes.utils import parse_type_from_str
|
9
|
+
from vellum.workflows.types.core import Json
|
10
|
+
|
11
|
+
|
12
|
+
class Person(BaseModel):
|
13
|
+
name: str
|
14
|
+
age: int
|
15
|
+
|
16
|
+
|
17
|
+
class FunctionCall(BaseModel):
|
18
|
+
name: str
|
19
|
+
args: List[int]
|
20
|
+
|
21
|
+
|
22
|
+
@pytest.mark.parametrize(
|
23
|
+
"input_str, output_type, expected_result",
|
24
|
+
[
|
25
|
+
("hello", str, "hello"),
|
26
|
+
("3.14", float, 3.14),
|
27
|
+
("42", int, 42),
|
28
|
+
("True", bool, True),
|
29
|
+
("", List[str], []), # Empty string should return an empty list
|
30
|
+
("[1, 2, 3]", List[int], [1, 2, 3]),
|
31
|
+
('["a", "b", "c"]', List[str], ["a", "b", "c"]),
|
32
|
+
('{"name": "Alice", "age": 30}', Json, {"name": "Alice", "age": 30}),
|
33
|
+
(
|
34
|
+
'{"type": "FUNCTION_CALL", "value": {"name": "test", "args": [1, 2]}}',
|
35
|
+
Json,
|
36
|
+
{"name": "test", "args": [1, 2]},
|
37
|
+
),
|
38
|
+
("42", Union[int, str], 42),
|
39
|
+
("hello", Union[int, str], "hello"),
|
40
|
+
],
|
41
|
+
ids=[
|
42
|
+
"str",
|
43
|
+
"float",
|
44
|
+
"int",
|
45
|
+
"bool",
|
46
|
+
"empty_list",
|
47
|
+
"list_of_int",
|
48
|
+
"list_of_str",
|
49
|
+
"simple_json",
|
50
|
+
"function_call_json",
|
51
|
+
"union_int",
|
52
|
+
"union_str",
|
53
|
+
],
|
54
|
+
)
|
55
|
+
def test_parse_type_from_str_basic_cases(input_str, output_type, expected_result):
|
56
|
+
result = parse_type_from_str(input_str, output_type)
|
57
|
+
assert result == expected_result
|
58
|
+
|
59
|
+
|
60
|
+
def test_parse_type_from_str_pydantic_models():
|
61
|
+
person_json = '{"name": "Alice", "age": 30}'
|
62
|
+
person = parse_type_from_str(person_json, Person)
|
63
|
+
assert isinstance(person, Person)
|
64
|
+
assert person.name == "Alice"
|
65
|
+
assert person.age == 30
|
66
|
+
|
67
|
+
function_json = '{"name": "test", "args": [1, 2]}'
|
68
|
+
function = parse_type_from_str(function_json, FunctionCall)
|
69
|
+
assert isinstance(function, FunctionCall)
|
70
|
+
assert function.name == "test"
|
71
|
+
assert function.args == [1, 2]
|
72
|
+
|
73
|
+
function_call_json = '{"value": {"name": "test", "args": [1, 2]}}'
|
74
|
+
function = parse_type_from_str(function_call_json, FunctionCall)
|
75
|
+
assert isinstance(function, FunctionCall)
|
76
|
+
assert function.name == "test"
|
77
|
+
assert function.args == [1, 2]
|
78
|
+
|
79
|
+
|
80
|
+
def test_parse_type_from_str_list_of_models():
|
81
|
+
person_list_json = '[{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]'
|
82
|
+
persons = parse_type_from_str(person_list_json, List[Person])
|
83
|
+
assert len(persons) == 2
|
84
|
+
assert all(isinstance(p, Person) for p in persons)
|
85
|
+
assert persons[0].name == "Alice"
|
86
|
+
assert persons[0].age == 30
|
87
|
+
assert persons[1].name == "Bob"
|
88
|
+
assert persons[1].age == 25
|
89
|
+
|
90
|
+
|
91
|
+
@pytest.mark.parametrize(
|
92
|
+
"input_str, output_type, expected_exception, expected_code, expected_message",
|
93
|
+
[
|
94
|
+
(
|
95
|
+
"{invalid json}",
|
96
|
+
List[str],
|
97
|
+
NodeException,
|
98
|
+
WorkflowErrorCode.INVALID_OUTPUTS,
|
99
|
+
"Invalid JSON Array format for result_as_str",
|
100
|
+
),
|
101
|
+
(
|
102
|
+
"{invalid json}",
|
103
|
+
Person,
|
104
|
+
NodeException,
|
105
|
+
WorkflowErrorCode.INVALID_OUTPUTS,
|
106
|
+
"Invalid JSON format for result_as_str",
|
107
|
+
),
|
108
|
+
(
|
109
|
+
"{invalid json}",
|
110
|
+
Json,
|
111
|
+
NodeException,
|
112
|
+
WorkflowErrorCode.INVALID_OUTPUTS,
|
113
|
+
"Invalid JSON format for result_as_str",
|
114
|
+
),
|
115
|
+
('{"name": "Alice"}', List[str], ValueError, None, "Expected a list of items for result_as_str, received dict"),
|
116
|
+
("data", object, ValueError, None, "Unsupported output type: <class 'object'>"),
|
117
|
+
],
|
118
|
+
ids=[
|
119
|
+
"invalid_json_list",
|
120
|
+
"invalid_json_model",
|
121
|
+
"invalid_json_json_type",
|
122
|
+
"non_list_for_list",
|
123
|
+
"unsupported_type",
|
124
|
+
],
|
125
|
+
)
|
126
|
+
def test_parse_type_from_str_error_cases(input_str, output_type, expected_exception, expected_code, expected_message):
|
127
|
+
with pytest.raises(expected_exception) as excinfo:
|
128
|
+
parse_type_from_str(input_str, output_type)
|
129
|
+
|
130
|
+
if expected_code:
|
131
|
+
assert excinfo.value.code == expected_code
|
132
|
+
|
133
|
+
assert expected_message in str(excinfo.value)
|
vellum/workflows/nodes/utils.py
CHANGED
@@ -95,10 +95,18 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
|
|
95
95
|
return bool(result_as_str)
|
96
96
|
|
97
97
|
if get_origin(output_type) is list:
|
98
|
+
# Handle empty string case for list types by returning an empty list
|
99
|
+
if not result_as_str.strip():
|
100
|
+
return []
|
101
|
+
|
98
102
|
try:
|
99
103
|
data = json.loads(result_as_str)
|
100
104
|
except json.JSONDecodeError:
|
101
|
-
raise ValueError("Invalid JSON Array format for result_as_str")
|
105
|
+
# raise ValueError("Invalid JSON Array format for result_as_str")
|
106
|
+
raise NodeException(
|
107
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
108
|
+
message="Invalid JSON Array format for result_as_str",
|
109
|
+
)
|
102
110
|
|
103
111
|
if not isinstance(data, list):
|
104
112
|
raise ValueError(f"Expected a list of items for result_as_str, received {data.__class__.__name__}")
|
@@ -117,7 +125,10 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
|
|
117
125
|
return data["value"]
|
118
126
|
return data
|
119
127
|
except json.JSONDecodeError:
|
120
|
-
raise
|
128
|
+
raise NodeException(
|
129
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
130
|
+
message="Invalid JSON format for result_as_str",
|
131
|
+
)
|
121
132
|
|
122
133
|
if get_origin(output_type) is Union:
|
123
134
|
for inner_type in get_args(output_type):
|
@@ -140,7 +151,10 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
|
|
140
151
|
data = data["value"]
|
141
152
|
return output_type.model_validate(data)
|
142
153
|
except json.JSONDecodeError:
|
143
|
-
raise
|
154
|
+
raise NodeException(
|
155
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
156
|
+
message="Invalid JSON format for result_as_str",
|
157
|
+
)
|
144
158
|
|
145
159
|
raise ValueError(f"Unsupported output type: {output_type}")
|
146
160
|
|
@@ -200,7 +200,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
200
200
|
parent=parent_context,
|
201
201
|
)
|
202
202
|
node_run_response: NodeRunResponse
|
203
|
-
was_mocked =
|
203
|
+
was_mocked: Optional[bool] = None
|
204
204
|
mock_candidates = self.workflow.context.node_output_mocks_map.get(node.Outputs) or []
|
205
205
|
for mock_candidate in mock_candidates:
|
206
206
|
if mock_candidate.when_condition.resolve(node.state):
|
@@ -315,6 +315,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
315
315
|
node_definition=node.__class__,
|
316
316
|
outputs=outputs,
|
317
317
|
invoked_ports=invoked_ports,
|
318
|
+
mocked=was_mocked,
|
318
319
|
),
|
319
320
|
parent=parent_context,
|
320
321
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vellum-ai
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.15
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -33,7 +33,7 @@ Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
|
|
33
33
|
Requires-Dist: pydash (==7.0.6)
|
34
34
|
Requires-Dist: python-dotenv (==1.0.1)
|
35
35
|
Requires-Dist: pytz (==2025.1)
|
36
|
-
Requires-Dist: pyyaml (==6.0.
|
36
|
+
Requires-Dist: pyyaml (==6.0.2)
|
37
37
|
Requires-Dist: requests (==2.32.3)
|
38
38
|
Requires-Dist: tomli (==2.0.2)
|
39
39
|
Requires-Dist: typing_extensions (>=4.0.0)
|
@@ -23,16 +23,16 @@ vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
23
23
|
vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
vellum_ee/workflows/display/base.py,sha256=ak29FIsawhaFa9_paZUHThlZRFJ1xB486JWKuSt1PYY,1965
|
25
25
|
vellum_ee/workflows/display/nodes/__init__.py,sha256=436iSAh_Ex5tC68oEYvNgPu05ZVIAVXnS4PKGrQeZ0Y,321
|
26
|
-
vellum_ee/workflows/display/nodes/base_node_display.py,sha256=
|
27
|
-
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=
|
28
|
-
vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=
|
26
|
+
vellum_ee/workflows/display/nodes/base_node_display.py,sha256=pYmIdmC2dxLyRlZEBlgARIJMLcBX3GxZPpWZISv5Bbg,17170
|
27
|
+
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=ZLKQ8Xa3h9nGkj4t4V_7OeU7CBFWY3gXB9CkaCLOhEk,2699
|
28
|
+
vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=RWCGm29-Tabi-qgVEIi_sdTWBv4bEzxAgwAoYI5T-Cc,2566
|
29
29
|
vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
30
|
vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=QqR3Ly0RNrXwOeLdW5nERDFt0gRPf76n1bPES6o5UN4,1093
|
31
31
|
vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
|
32
32
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
33
33
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
|
34
34
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=hoV-cUtS6H9kmRQXHd2py95GRWI_dAnnaPwvlNBkDOQ,8571
|
35
|
-
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=
|
35
|
+
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=t-KSaai6cXRLkNkzCvEbM5SHh03B9fqPwVTH-Gei_-0,4419
|
36
36
|
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=z00Z3L0d4PsUQo4S8FRDTtOFLtjdi17TJbatNVF4nM8,4288
|
37
37
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=ybLIa4uclqVIy3VAQvI1ivg2tnK5Ug_1R5a69DFqL7E,11104
|
38
38
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=I1Jkp2htRINJATtv1e-zs9BrReFX842djpiVgBPHDYg,2186
|
@@ -44,27 +44,27 @@ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=8CPnn06HIBxBOiECevUf
|
|
44
44
|
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=HkNMgdQELiON42jdO-xDLmqrEKdGx1RVqrz2DXNTLS8,3239
|
45
45
|
vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=TMb8txILu2uWjzoxaghjgjlzeBAgzn4vkP_8zSh2qoE,1151
|
46
46
|
vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=LFjLUrH6sJ4czPnExdRqFr0PB_yKBMLXLvK5GAzIAgc,3273
|
47
|
-
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256
|
47
|
+
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=LgokATi7sSS38Fil-XjqoR4t7AMOJ-GzXRw6p606Svo,3397
|
48
48
|
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=TxcAGZDl_hvJ7Y1hUi9YVEVrj9Ie0hKkASdpfRL4_cs,9227
|
49
49
|
vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=62baAElKoRKIoba0lLhnrXGWWx96B73VxKGxh7BaIxc,2612
|
50
|
-
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=
|
50
|
+
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=wP-ZAZq6ZmkS1fj0WkFtRkYqi8c6MWKLh4CEw8JD9mg,3207
|
51
51
|
vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=ulrpoYUW-5kIxfG4Lf5F2p0k_EoYKhmahEbF3P_eruM,1648
|
53
53
|
vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=bg9INsXiWfyK047u8TD1oEOFYrqDq8GC7Hvgz69n7BE,1988
|
54
54
|
vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=NuIw8Yb42KUdoGi3Ur8_7VPg50IC4hNrwAkCociwqNk,2091
|
55
55
|
vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py,sha256=mtzB8LJlFCHVFM4H5AanLp29gQfaVmnN4A4iaRGJHoI,2427
|
56
56
|
vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=4YUaTeD_OWF-UaPMyOTBTu9skGC1jgSHlAYrzbH7Z04,5039
|
57
|
-
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=
|
57
|
+
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=0ipBZMYm521tuwefQFgHOvTyTgiWVTkzxpQtnnmOAI0,4203
|
58
58
|
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=F_0BrlSszllK_BhryPbojIleLq2dGXOfQD1rVp3fNFg,4733
|
59
59
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
|
-
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=
|
60
|
+
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=VD-4USiRlCcdC3Qe9WfdkxwFdircai0vqvuZCbELR84,9556
|
61
61
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
62
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
63
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=A1-tIpC5KIKG9JA_rkd1nLS8zUG3Kb4QiVdvb3boFxE,2509
|
64
64
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=mFmxXfnUPrwndaBurW9E-VSBUQjF3cGv3JpRuNmwWh8,15475
|
65
65
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=1cszL6N6FNGVm61MOa7AEiHnF0QjZWqDQuPOp4yiG94,18277
|
66
66
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=-12ZkZb3f5gyoNASV2yeQtMo5HmNsVEo8nXwL6IC-I8,6261
|
67
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=
|
67
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=FD94h96w5wWYlLVC8_tqr2aUHPyO2UQh4f03jDX4re4,39783
|
68
68
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=EbVgg_3_ipTt3MOop4RARX0fmNjwqZtkhIXzx9nGw7Y,4487
|
69
69
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=IRazH2QR6F8RGqNemEnHueyj5DtEa6rFTYhT16S4jI8,15917
|
70
70
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=V__y7uu-dy6TJjPeu4UDvaoO2yYwBRbPiW9uJdzWRx4,29828
|
@@ -87,12 +87,12 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_n
|
|
87
87
|
vellum_ee/workflows/display/types.py,sha256=ixfmcQn51Rhsm4_0hWfG0_WpzLE89ZrDZpeYBklsP1Q,2592
|
88
88
|
vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
89
89
|
vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh4aTVnh7CHR2ykyMUDw0,1151
|
90
|
-
vellum_ee/workflows/display/utils/vellum.py,sha256=
|
90
|
+
vellum_ee/workflows/display/utils/vellum.py,sha256=EVPQUSsZ3OIeLTEbV6LHPor37t9fnj9kJxDqP4PmTLQ,8234
|
91
91
|
vellum_ee/workflows/display/vellum.py,sha256=7mqQaKZPPrLMcXSAQkPIxCy5x8HkKs5PbCu3GRaC2o8,8507
|
92
92
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
|
93
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
93
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=QKMSmV--UoE8L7pYmKvZqtaatygrxmSm9PDSH71Yr0Y,19937
|
94
94
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
|
95
|
-
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=
|
95
|
+
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=TzLziIh1fJ429LRUYoe88yd_HZflEQAw60TW8PAvvnk,6917
|
96
96
|
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=mbAzCpswOek34ITeTkesbVreCXpulj4NFjIg3RcdVZ8,18243
|
97
97
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
98
98
|
vellum_ee/workflows/server/virtual_file_loader.py,sha256=X_DdNK7MfyOjKWekk6YQpOSCT6klKcdjT6nVJcBH1sM,1481
|
@@ -123,12 +123,12 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
123
123
|
vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
|
124
124
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
125
125
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
126
|
-
vellum/client/core/client_wrapper.py,sha256=
|
126
|
+
vellum/client/core/client_wrapper.py,sha256=x6cZlx3EA5bHNlGRnX0klOVCrqkvqWv_oUW4xjYEehU,1869
|
127
127
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
128
128
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
129
129
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
130
130
|
vellum/client/core/jsonable_encoder.py,sha256=qaF1gtgH-kQZb4kJskETwcCsOPUof-NnYVdszHkb-dM,3656
|
131
|
-
vellum/client/core/pydantic_utilities.py,sha256=
|
131
|
+
vellum/client/core/pydantic_utilities.py,sha256=6ev3gtER-hjlq7PcPL9XT_YSCdgyCE8ZKHJ9Uc-gHIg,12071
|
132
132
|
vellum/client/core/query_encoder.py,sha256=ekulqNd0j8TgD7ox-Qbz7liqX8-KP9blvT9DsRCenYM,2144
|
133
133
|
vellum/client/core/remove_none_from_dict.py,sha256=EU9SGgYidWq7SexuJbNs4-PZ-5Bl3Vppd864mS6vQZw,342
|
134
134
|
vellum/client/core/request_options.py,sha256=5cCGt5AEGgtP5xifDl4oVQUmSjlIA8FmRItAlJawM18,1417
|
@@ -367,7 +367,7 @@ vellum/client/types/json_input.py,sha256=ZUA2O9YueBCx0IMMdB8uYNSWJiSDZxMm5ogwbwC
|
|
367
367
|
vellum/client/types/json_input_request.py,sha256=x5sA-VXxF4QH-98xRcIKPZhsMVbnJNUQofiUQqyfGk4,768
|
368
368
|
vellum/client/types/json_vellum_value.py,sha256=8irlw6NkRRVafysfTc1Q5BFFhRrWJYzdwrDYTdJK4JY,689
|
369
369
|
vellum/client/types/json_vellum_value_request.py,sha256=IUlkdwFGgBeLl9sCmAJhoaxomWiEMpWgRcLa_WnlK8g,696
|
370
|
-
vellum/client/types/logical_operator.py,sha256=
|
370
|
+
vellum/client/types/logical_operator.py,sha256=_3-gBnfsz4g5-sw5_ThRuS0NdNLQ2n2vV2Dl1RqbXe4,617
|
371
371
|
vellum/client/types/logprobs_enum.py,sha256=D_458cZX2CAb6dX_ovrQ6HARlJkYcZRadKwsi1Cr-JM,151
|
372
372
|
vellum/client/types/map_node_result.py,sha256=e2YqEP-aKig2TbbZIlfn5LCeVEVhEJJBR7o7xTBKXaY,752
|
373
373
|
vellum/client/types/map_node_result_data.py,sha256=3xJXC1JrS9lo3-3_u1S79sYwLxMknNntDyspN24vQzY,699
|
@@ -720,7 +720,7 @@ vellum/evaluations/utils/exceptions.py,sha256=dXMAkzqbHV_AP5FjjbegPlfUE0zQDlpA3q
|
|
720
720
|
vellum/evaluations/utils/paginator.py,sha256=rEED_BJAXAM6tM1yMwHePNzszjq_tTq4NbQvi1jWQ_Q,697
|
721
721
|
vellum/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
722
722
|
vellum/plugins/pydantic.py,sha256=BMUwC4OkxtNf6hpLwb0T4MBUKgkGy55T_5Ww7GHpHYo,3068
|
723
|
-
vellum/plugins/utils.py,sha256=
|
723
|
+
vellum/plugins/utils.py,sha256=cPmxE9R2CK1bki2jKE8rB-G9zMf2pzHjSPDHFPXwd3Q,878
|
724
724
|
vellum/plugins/vellum_mypy.py,sha256=QTuMSq6PiZW1dyTUZ5Bf1d4XkgFj0TKAgZLP8f4UgL4,27914
|
725
725
|
vellum/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
726
726
|
vellum/prompts/blocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1312,9 +1312,9 @@ vellum/workflows/environment/environment.py,sha256=0XhJPBs8YASWmvPx8bkSdCvcbDmzp
|
|
1312
1312
|
vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
|
1313
1313
|
vellum/workflows/errors/types.py,sha256=tVW7Il9zalnwWzdoDLqYPIvRTOhXIv6FPORZAbU7n5Q,3640
|
1314
1314
|
vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyuUK48Qw,759
|
1315
|
-
vellum/workflows/events/node.py,sha256=
|
1315
|
+
vellum/workflows/events/node.py,sha256=jbmNHjdp331Q1IRK-AWtAxwF6Lidb9R7__N5rQuilE8,5401
|
1316
1316
|
vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1317
|
-
vellum/workflows/events/tests/test_event.py,sha256=
|
1317
|
+
vellum/workflows/events/tests/test_event.py,sha256=sHcKhZPDPtzZfTmehL4NORA_StR4M6nZDcx9kz3Avo0,16866
|
1318
1318
|
vellum/workflows/events/types.py,sha256=AeTJaQt_fNHDLI4nyBzo7XrW9QQybRC09AKzu3kEYEE,3575
|
1319
1319
|
vellum/workflows/events/workflow.py,sha256=QoSHyIOpuVacbR7POf7h104miTOhCjtO2udnYximJGs,6851
|
1320
1320
|
vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
|
@@ -1380,10 +1380,10 @@ vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=uMR0AyIFn539LqTKH
|
|
1380
1380
|
vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
|
1381
1381
|
vellum/workflows/nodes/core/retry_node/node.py,sha256=Vt3fx4G-DRIb9a-IHIUfaAclgfbzOPEQVkcumwhl9HE,4355
|
1382
1382
|
vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1383
|
-
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=
|
1383
|
+
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=fNgDufkIsrTC-6ftvogqSpWhqqBj9iNESdfK19B1Yx0,5159
|
1384
1384
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1385
1385
|
vellum/workflows/nodes/core/templating_node/node.py,sha256=-JIqLUv6Xpx_LTVZt7whQ2X2VatgHDdTxjMrz64luEs,3721
|
1386
|
-
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=
|
1386
|
+
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=MHofz-BwAgt7EXkab8VIyacYznDEIJ7Er7MJUaxNQQo,9614
|
1387
1387
|
vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
|
1388
1388
|
vellum/workflows/nodes/core/try_node/node.py,sha256=5ux1l2HO12FBFFyhz6j-4yfBYVrqgT2maTAne_GnNDk,4434
|
1389
1389
|
vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1392,10 +1392,10 @@ vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDX
|
|
1392
1392
|
vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
|
1393
1393
|
vellum/workflows/nodes/displayable/api_node/node.py,sha256=QdpsyGVxo5PcN8nwGZpcpW_YMKHr3_VvmbK1BlrdOFk,2547
|
1394
1394
|
vellum/workflows/nodes/displayable/api_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1395
|
-
vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=
|
1395
|
+
vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=fiskhfcI4c6CxFlbSWb1JKsuq-98zAeUWOExc848ALw,3130
|
1396
1396
|
vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN03vWGMuI1WrrLZeMLT2Cl2c,304
|
1397
1397
|
vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
|
1398
|
-
vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256
|
1398
|
+
vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=Ev_So7D_4Qfvl2_E8veVfxAxWfbJIA2ujyW5istLg5I,4066
|
1399
1399
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
|
1400
1400
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=nvhoWb8EyRlgtyotYp-wh194n30yQP81UnOH_a8FghY,3140
|
1401
1401
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
|
@@ -1453,7 +1453,8 @@ vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=1
|
|
1453
1453
|
vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
|
1454
1454
|
vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1455
1455
|
vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
|
1456
|
-
vellum/workflows/nodes/
|
1456
|
+
vellum/workflows/nodes/tests/test_utils.py,sha256=7PtdV_fzl56agx0IDitdqOmqUO9cQZmJww-3ToxzSqA,4174
|
1457
|
+
vellum/workflows/nodes/utils.py,sha256=b-U8xjUpGswaoEiav5tU_OFKB26GkYFzuko9XCMU_Fo,7627
|
1457
1458
|
vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
|
1458
1459
|
vellum/workflows/outputs/base.py,sha256=b4Dnha1miKu3uFJYptKKflIHNuajPF2BNKy0BTt8Tjc,8622
|
1459
1460
|
vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
|
@@ -1475,7 +1476,7 @@ vellum/workflows/references/workflow_input.py,sha256=86IuhlBz-9cGxeUzizyjdp482aj
|
|
1475
1476
|
vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
|
1476
1477
|
vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
|
1477
1478
|
vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
|
1478
|
-
vellum/workflows/runner/runner.py,sha256=
|
1479
|
+
vellum/workflows/runner/runner.py,sha256=fS21u0LIyhxT4T6YQPgonVuH8X8TgD5BrsbVxPUWfuM,31071
|
1479
1480
|
vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
|
1480
1481
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
1481
1482
|
vellum/workflows/state/base.py,sha256=Vkhneko3VlQrPsMLU1PYSzXU_W1u7_AraJsghiv5O-4,15512
|
@@ -1511,8 +1512,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1511
1512
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1512
1513
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=NRteiICyJvDM5zrtUfq2fZoXcGQVaWC9xmNlLLVW0cU,7979
|
1513
1514
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1514
|
-
vellum_ai-0.14.
|
1515
|
-
vellum_ai-0.14.
|
1516
|
-
vellum_ai-0.14.
|
1517
|
-
vellum_ai-0.14.
|
1518
|
-
vellum_ai-0.14.
|
1515
|
+
vellum_ai-0.14.15.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1516
|
+
vellum_ai-0.14.15.dist-info/METADATA,sha256=8TM16vJdsQpUXVDgasscuRmbLESHxIgmMj8QlKoLUQU,5408
|
1517
|
+
vellum_ai-0.14.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1518
|
+
vellum_ai-0.14.15.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1519
|
+
vellum_ai-0.14.15.dist-info/RECORD,,
|
@@ -4,6 +4,7 @@ from uuid import UUID
|
|
4
4
|
from typing import (
|
5
5
|
TYPE_CHECKING,
|
6
6
|
Any,
|
7
|
+
ClassVar,
|
7
8
|
Dict,
|
8
9
|
ForwardRef,
|
9
10
|
Generic,
|
@@ -59,17 +60,20 @@ _NodeDisplayAttrType = TypeVar("_NodeDisplayAttrType")
|
|
59
60
|
class BaseNodeDisplayMeta(type):
|
60
61
|
def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
|
61
62
|
cls = super().__new__(mcs, name, bases, dct)
|
63
|
+
return cls.__annotate_node__()
|
64
|
+
|
65
|
+
def __annotate_node__(cls):
|
62
66
|
base_node_display_class = cast(Type["BaseNodeDisplay"], cls)
|
63
67
|
node_class = base_node_display_class.infer_node_class()
|
64
68
|
if not issubclass(node_class, BaseNode):
|
65
69
|
return cls
|
66
70
|
|
67
|
-
display_node_id =
|
71
|
+
display_node_id = getattr(cls, "node_id", None)
|
68
72
|
if isinstance(display_node_id, UUID):
|
69
73
|
# Display classes are able to override the id of the node class it's parameterized by
|
70
74
|
node_class.__id__ = display_node_id
|
71
75
|
|
72
|
-
output_display =
|
76
|
+
output_display = getattr(cls, "output_display", None)
|
73
77
|
if isinstance(output_display, dict):
|
74
78
|
# And the node class' output ids
|
75
79
|
for reference, node_output_display in output_display.items():
|
@@ -86,6 +90,7 @@ class BaseNodeDisplayMeta(type):
|
|
86
90
|
class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
87
91
|
output_display: Dict[OutputReference, NodeOutputDisplay] = {}
|
88
92
|
port_displays: Dict[Port, PortDisplayOverrides] = {}
|
93
|
+
node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
89
94
|
|
90
95
|
# Used to store the mapping between node types and their display classes
|
91
96
|
_node_display_registry: Dict[Type[NodeType], Type["BaseNodeDisplay"]] = {}
|
@@ -326,6 +331,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
326
331
|
IsNotNilExpression,
|
327
332
|
IsUndefinedExpression,
|
328
333
|
IsNotUndefinedExpression,
|
334
|
+
ParseJsonExpression,
|
329
335
|
),
|
330
336
|
):
|
331
337
|
lhs = self.serialize_value(display_context, condition._expression)
|
@@ -403,9 +409,6 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
403
409
|
"node_id": str(node_class_display.node_id),
|
404
410
|
}
|
405
411
|
|
406
|
-
if isinstance(value, ParseJsonExpression):
|
407
|
-
raise ValueError("ParseJsonExpression is not supported in the UI")
|
408
|
-
|
409
412
|
if not isinstance(value, BaseDescriptor):
|
410
413
|
vellum_value = primitive_to_vellum_value(value)
|
411
414
|
return {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import ClassVar, Dict, Optional
|
2
|
+
from typing import ClassVar, Dict, Optional
|
3
3
|
|
4
4
|
from vellum.workflows.nodes.utils import get_unadorned_node
|
5
5
|
from vellum.workflows.ports import Port
|
@@ -17,9 +17,6 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
|
17
17
|
# Used to explicitly set the target handle id for a node
|
18
18
|
target_handle_id: ClassVar[Optional[UUID]] = None
|
19
19
|
|
20
|
-
# Used to explicitly set the node input ids by name for a node
|
21
|
-
node_input_ids_by_name: ClassVar[Dict[str, Union[UUID, str]]] = {}
|
22
|
-
|
23
20
|
def _get_node_display_uuid(self, attribute: str) -> UUID:
|
24
21
|
explicit_value = self._get_explicit_node_display_attr(attribute, UUID)
|
25
22
|
return explicit_value if explicit_value else uuid4_from_hash(f"{self.node_id}|{attribute}")
|
@@ -1,7 +1,10 @@
|
|
1
1
|
import types
|
2
|
-
from
|
2
|
+
from uuid import UUID
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Type
|
3
4
|
|
5
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
4
6
|
from vellum.workflows.types.generics import NodeType
|
7
|
+
from vellum.workflows.utils.uuids import uuid4_from_hash
|
5
8
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
6
9
|
|
7
10
|
if TYPE_CHECKING:
|
@@ -26,16 +29,39 @@ def get_node_display_class(
|
|
26
29
|
|
27
30
|
# `base_node_display_class` is always a Generic class, so it's safe to index into it
|
28
31
|
NodeDisplayBaseClass = base_node_display_class[node_class] # type: ignore[index]
|
32
|
+
|
33
|
+
def _get_node_input_ids_by_ref(path: str, inst: Any):
|
34
|
+
if isinstance(inst, dict):
|
35
|
+
node_input_ids_by_name: Dict[str, UUID] = {}
|
36
|
+
for key, value in inst.items():
|
37
|
+
node_input_ids_by_name.update(_get_node_input_ids_by_ref(f"{path}.{key}", value))
|
38
|
+
return node_input_ids_by_name
|
39
|
+
|
40
|
+
if isinstance(inst, BaseDescriptor):
|
41
|
+
return {path: uuid4_from_hash(f"{node_class.__id__}|{path}")}
|
42
|
+
|
43
|
+
return {}
|
44
|
+
|
45
|
+
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
|
+
node_input_ids_by_name: Dict[str, UUID] = {}
|
55
|
+
for ref in node_class:
|
56
|
+
node_input_ids_by_name.update(_get_node_input_ids_by_ref(ref.name, ref.instance))
|
57
|
+
|
58
|
+
if node_input_ids_by_name:
|
59
|
+
ns["node_input_ids_by_name"] = node_input_ids_by_name
|
60
|
+
|
29
61
|
NodeDisplayClass = types.new_class(
|
30
62
|
f"{node_class.__name__}Display",
|
31
63
|
bases=(NodeDisplayBaseClass,),
|
64
|
+
exec_body=exec_body,
|
32
65
|
)
|
33
|
-
output_display = {
|
34
|
-
ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
|
35
|
-
for ref in node_class.Outputs
|
36
|
-
if ref.name in node_class.__output_ids__
|
37
|
-
}
|
38
|
-
if output_display:
|
39
|
-
setattr(NodeDisplayClass, "output_display", output_display)
|
40
66
|
|
41
67
|
return NodeDisplayClass
|
@@ -1,9 +1,14 @@
|
|
1
|
+
import re
|
2
|
+
import types
|
1
3
|
from uuid import UUID
|
2
|
-
from typing import Any, Callable, Generic, Optional, TypeVar, cast
|
4
|
+
from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar, cast
|
3
5
|
|
6
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
4
7
|
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
5
8
|
from vellum.workflows.nodes.utils import get_wrapped_node
|
6
9
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
10
|
+
from vellum.workflows.types.utils import get_original_base
|
11
|
+
from vellum.workflows.utils.uuids import uuid4_from_hash
|
7
12
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
8
13
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
9
14
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
@@ -37,3 +42,52 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
37
42
|
serialized_wrapped_node["adornments"] = adornments + [adornment] if adornment else adornments
|
38
43
|
|
39
44
|
return serialized_wrapped_node
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def wrap(cls, **kwargs: Any) -> Callable[..., Type[BaseNodeDisplay]]:
|
48
|
+
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
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__)
|
53
|
+
|
54
|
+
# `mypy` is wrong here, `cls` is indexable bc it's Generic
|
55
|
+
BaseAdornmentDisplay = cls[node_class] # type: ignore[index]
|
56
|
+
|
57
|
+
def exec_body(ns: Dict):
|
58
|
+
for key, kwarg in kwargs.items():
|
59
|
+
ns[key] = kwarg
|
60
|
+
|
61
|
+
if "node_id" not in kwargs:
|
62
|
+
ns["node_id"] = uuid4_from_hash(node_class.__qualname__)
|
63
|
+
|
64
|
+
AdornmentDisplay = types.new_class(
|
65
|
+
re.sub(r"^Base", "", cls.__name__), bases=(BaseAdornmentDisplay,), exec_body=exec_body
|
66
|
+
)
|
67
|
+
|
68
|
+
setattr(inner_cls, "__adorned_by__", AdornmentDisplay)
|
69
|
+
|
70
|
+
# We must edit the node display class to use __wrapped_node__ everywhere it
|
71
|
+
# references the adorned node class, which is three places:
|
72
|
+
|
73
|
+
# 1. The node display class' parameterized type
|
74
|
+
original_base_node_display = get_original_base(inner_cls)
|
75
|
+
original_base_node_display.__args__ = (wrapped_node_class,)
|
76
|
+
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
77
|
+
inner_cls.__annotate_node__()
|
78
|
+
|
79
|
+
# 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
|
+
|
85
|
+
# 3. The node display class' port displays
|
86
|
+
old_ports = list(inner_cls.port_displays.keys())
|
87
|
+
for old_port in old_ports:
|
88
|
+
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
89
|
+
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
90
|
+
|
91
|
+
return inner_cls
|
92
|
+
|
93
|
+
return decorator
|
@@ -1,14 +1,12 @@
|
|
1
1
|
import inspect
|
2
|
-
from typing import Any,
|
2
|
+
from typing import Any, Generic, Tuple, Type, TypeVar, cast
|
3
3
|
|
4
4
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
5
|
-
from vellum.workflows.errors.types import WorkflowErrorCode
|
6
5
|
from vellum.workflows.nodes.bases.base import BaseNode
|
7
6
|
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
8
7
|
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
9
8
|
from vellum.workflows.references.output import OutputReference
|
10
9
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
11
|
-
from vellum.workflows.types.utils import get_original_base
|
12
10
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
13
11
|
from vellum.workflows.workflows.base import BaseWorkflow
|
14
12
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
@@ -79,54 +77,3 @@ class BaseRetryNodeDisplay(BaseAdornmentNodeDisplay[_RetryNodeType], Generic[_Re
|
|
79
77
|
|
80
78
|
inner_output = getattr(inner_node.Outputs, output.name)
|
81
79
|
return node_display.get_node_output_display(inner_output)
|
82
|
-
|
83
|
-
@classmethod
|
84
|
-
def wrap(
|
85
|
-
cls,
|
86
|
-
max_attempts: int,
|
87
|
-
delay: Optional[float] = None,
|
88
|
-
retry_on_error_code: Optional[WorkflowErrorCode] = None,
|
89
|
-
retry_on_condition: Optional[BaseDescriptor] = None,
|
90
|
-
) -> Callable[..., Type["BaseRetryNodeDisplay"]]:
|
91
|
-
_max_attempts = max_attempts
|
92
|
-
_delay = delay
|
93
|
-
_retry_on_error_code = retry_on_error_code
|
94
|
-
_retry_on_condition = retry_on_condition
|
95
|
-
|
96
|
-
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
97
|
-
|
98
|
-
def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
|
99
|
-
node_class = inner_cls.infer_node_class()
|
100
|
-
wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
|
101
|
-
|
102
|
-
class RetryNodeDisplay(BaseRetryNodeDisplay[node_class]): # type: ignore[valid-type]
|
103
|
-
max_attempts = _max_attempts
|
104
|
-
delay = _delay
|
105
|
-
retry_on_error_code = _retry_on_error_code
|
106
|
-
retry_on_condition = _retry_on_condition
|
107
|
-
|
108
|
-
setattr(inner_cls, "__adorned_by__", RetryNodeDisplay)
|
109
|
-
|
110
|
-
# We must edit the node display class to use __wrapped_node__ everywhere it
|
111
|
-
# references the adorned node class, which is three places:
|
112
|
-
|
113
|
-
# 1. The node display class' parameterized type
|
114
|
-
original_base_node_display = get_original_base(inner_cls)
|
115
|
-
original_base_node_display.__args__ = (wrapped_node_class,)
|
116
|
-
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
117
|
-
|
118
|
-
# 2. The node display class' output displays
|
119
|
-
old_outputs = list(inner_cls.output_display.keys())
|
120
|
-
for old_output in old_outputs:
|
121
|
-
new_output = getattr(wrapped_node_class.Outputs, old_output.name)
|
122
|
-
inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
|
123
|
-
|
124
|
-
# 3. The node display class' port displays
|
125
|
-
old_ports = list(inner_cls.port_displays.keys())
|
126
|
-
for old_port in old_ports:
|
127
|
-
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
128
|
-
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
129
|
-
|
130
|
-
return inner_cls
|
131
|
-
|
132
|
-
return decorator
|
@@ -23,14 +23,7 @@ class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Gene
|
|
23
23
|
node = self._node
|
24
24
|
node_id = self.node_id
|
25
25
|
|
26
|
-
template_input_id = self.template_input_id or
|
27
|
-
(
|
28
|
-
UUID(input_id) if isinstance(input_id, str) else input_id
|
29
|
-
for input_name, input_id in self.node_input_ids_by_name.items()
|
30
|
-
if input_name == TEMPLATE_INPUT_NAME
|
31
|
-
),
|
32
|
-
None,
|
33
|
-
)
|
26
|
+
template_input_id = self.template_input_id or self.node_input_ids_by_name.get(TEMPLATE_INPUT_NAME)
|
34
27
|
|
35
28
|
template_node_input = create_node_input(
|
36
29
|
node_id=node_id,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import inspect
|
2
2
|
from uuid import UUID
|
3
|
-
from typing import Any,
|
3
|
+
from typing import Any, ClassVar, Generic, Optional, Tuple, Type, TypeVar, cast
|
4
4
|
|
5
5
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
6
6
|
from vellum.workflows.nodes.bases.base import BaseNode
|
@@ -8,7 +8,6 @@ from vellum.workflows.nodes.core.try_node.node import TryNode
|
|
8
8
|
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
9
9
|
from vellum.workflows.references.output import OutputReference
|
10
10
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
11
|
-
from vellum.workflows.types.utils import get_original_base
|
12
11
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
13
12
|
from vellum.workflows.workflows.base import BaseWorkflow
|
14
13
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
@@ -93,43 +92,3 @@ class BaseTryNodeDisplay(BaseAdornmentNodeDisplay[_TryNodeType], Generic[_TryNod
|
|
93
92
|
|
94
93
|
inner_output = getattr(inner_node.Outputs, output.name)
|
95
94
|
return node_display.get_node_output_display(inner_output)
|
96
|
-
|
97
|
-
@classmethod
|
98
|
-
def wrap(cls, error_output_id: Optional[UUID] = None) -> Callable[..., Type["BaseTryNodeDisplay"]]:
|
99
|
-
_error_output_id = error_output_id
|
100
|
-
|
101
|
-
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
102
|
-
|
103
|
-
def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
|
104
|
-
node_class = inner_cls.infer_node_class()
|
105
|
-
wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
|
106
|
-
|
107
|
-
# Mypy gets mad about dynamic parameter types like this, but it's fine
|
108
|
-
class TryNodeDisplay(BaseTryNodeDisplay[node_class]): # type: ignore[valid-type]
|
109
|
-
error_output_id = _error_output_id
|
110
|
-
|
111
|
-
setattr(inner_cls, "__adorned_by__", TryNodeDisplay)
|
112
|
-
|
113
|
-
# We must edit the node display class to use __wrapped_node__ everywhere it
|
114
|
-
# references the adorned node class, which is three places:
|
115
|
-
|
116
|
-
# 1. The node display class' parameterized type
|
117
|
-
original_base_node_display = get_original_base(inner_cls)
|
118
|
-
original_base_node_display.__args__ = (wrapped_node_class,)
|
119
|
-
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
120
|
-
|
121
|
-
# 2. The node display class' output displays
|
122
|
-
old_outputs = list(inner_cls.output_display.keys())
|
123
|
-
for old_output in old_outputs:
|
124
|
-
new_output = getattr(wrapped_node_class.Outputs, old_output.name)
|
125
|
-
inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
|
126
|
-
|
127
|
-
# 3. The node display class' port displays
|
128
|
-
old_ports = list(inner_cls.port_displays.keys())
|
129
|
-
for old_port in old_ports:
|
130
|
-
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
131
|
-
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
132
|
-
|
133
|
-
return inner_cls
|
134
|
-
|
135
|
-
return decorator
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import pytest
|
2
1
|
from uuid import UUID
|
3
2
|
from typing import Dict
|
4
3
|
|
@@ -231,11 +230,7 @@ def test_vellum_workflow_display__serialize_with_unused_nodes_and_edges():
|
|
231
230
|
assert edge_found, "Edge between unused nodes NodeB and NodeC not found in serialized output"
|
232
231
|
|
233
232
|
|
234
|
-
def
|
235
|
-
"""
|
236
|
-
Test that verifies ParseJsonExpression is not yet supported in the UI.
|
237
|
-
This test should fail once UI support is added, at which point it should be updated.
|
238
|
-
"""
|
233
|
+
def test_vellum_workflow_display__serialize_with_parse_json_expression():
|
239
234
|
# GIVEN a workflow that uses the parse_json function
|
240
235
|
from vellum.workflows.references.constant import ConstantValueReference
|
241
236
|
|
@@ -249,13 +244,55 @@ def test_parse_json_not_supported_in_ui():
|
|
249
244
|
class Outputs(BaseWorkflow.Outputs):
|
250
245
|
final = JsonNode.Outputs.json_result
|
251
246
|
|
252
|
-
#
|
247
|
+
# AND a display class for this workflow
|
253
248
|
workflow_display = get_workflow_display(
|
254
249
|
base_display_class=VellumWorkflowDisplay,
|
255
250
|
workflow_class=Workflow,
|
256
251
|
)
|
257
252
|
|
258
|
-
|
259
|
-
|
253
|
+
# WHEN we serialize the workflow
|
254
|
+
exec_config = workflow_display.serialize()
|
255
|
+
|
256
|
+
# THEN the serialized workflow contains the parse_json expression
|
257
|
+
raw_data = exec_config["workflow_raw_data"]
|
258
|
+
assert isinstance(raw_data, dict)
|
260
259
|
|
261
|
-
|
260
|
+
nodes = raw_data["nodes"]
|
261
|
+
assert isinstance(nodes, list)
|
262
|
+
|
263
|
+
json_node = None
|
264
|
+
for node in nodes:
|
265
|
+
assert isinstance(node, dict)
|
266
|
+
definition = node.get("definition")
|
267
|
+
if node.get("type") == "GENERIC" and isinstance(definition, dict) and definition.get("name") == "JsonNode":
|
268
|
+
json_node = node
|
269
|
+
break
|
270
|
+
|
271
|
+
assert json_node is not None
|
272
|
+
|
273
|
+
outputs = json_node.get("outputs", [])
|
274
|
+
assert isinstance(outputs, list)
|
275
|
+
|
276
|
+
json_result = None
|
277
|
+
for output in outputs:
|
278
|
+
assert isinstance(output, dict)
|
279
|
+
if output.get("name") == "json_result":
|
280
|
+
json_result = output
|
281
|
+
break
|
282
|
+
|
283
|
+
assert json_result == {
|
284
|
+
"id": "44c7d94c-a76a-4151-9b95-85a31764f18f",
|
285
|
+
"name": "json_result",
|
286
|
+
"type": "JSON",
|
287
|
+
"value": {
|
288
|
+
"type": "UNARY_EXPRESSION",
|
289
|
+
"lhs": {
|
290
|
+
"type": "CONSTANT_VALUE",
|
291
|
+
"value": {
|
292
|
+
"type": "STRING",
|
293
|
+
"value": '{"key": "value"}',
|
294
|
+
},
|
295
|
+
},
|
296
|
+
"operator": "parseJson",
|
297
|
+
},
|
298
|
+
}
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py
CHANGED
@@ -1003,3 +1003,70 @@ def test_serialize_node__or_then_and(serialize_node):
|
|
1003
1003
|
serialized_node,
|
1004
1004
|
ignore_order=True,
|
1005
1005
|
)
|
1006
|
+
|
1007
|
+
|
1008
|
+
class ParseJsonGenericNode(BaseNode):
|
1009
|
+
class Ports(BaseNode.Ports):
|
1010
|
+
if_branch = Port.on_if(Inputs.input.parse_json().equals({"key": "value"}))
|
1011
|
+
|
1012
|
+
|
1013
|
+
def test_serialize_node__parse_json(serialize_node):
|
1014
|
+
input_id = uuid4()
|
1015
|
+
serialized_node = serialize_node(
|
1016
|
+
node_class=ParseJsonGenericNode,
|
1017
|
+
global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)},
|
1018
|
+
)
|
1019
|
+
|
1020
|
+
assert not DeepDiff(
|
1021
|
+
{
|
1022
|
+
"id": "60edd9b2-9bad-470f-832c-c5f1aa9a253e",
|
1023
|
+
"label": "ParseJsonGenericNode",
|
1024
|
+
"type": "GENERIC",
|
1025
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
1026
|
+
"base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
|
1027
|
+
"definition": {
|
1028
|
+
"name": "ParseJsonGenericNode",
|
1029
|
+
"module": [
|
1030
|
+
"vellum_ee",
|
1031
|
+
"workflows",
|
1032
|
+
"display",
|
1033
|
+
"tests",
|
1034
|
+
"workflow_serialization",
|
1035
|
+
"generic_nodes",
|
1036
|
+
"test_ports_serialization",
|
1037
|
+
],
|
1038
|
+
},
|
1039
|
+
"trigger": {"id": "0ccc19cb-174b-440b-8d08-6c84c571fb8f", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
1040
|
+
"ports": [
|
1041
|
+
{
|
1042
|
+
"id": "abe1e6db-14df-4d3c-b059-d17933ab8c02",
|
1043
|
+
"type": "IF",
|
1044
|
+
"name": "if_branch",
|
1045
|
+
"expression": {
|
1046
|
+
"type": "BINARY_EXPRESSION",
|
1047
|
+
"lhs": {
|
1048
|
+
"type": "UNARY_EXPRESSION",
|
1049
|
+
"lhs": {
|
1050
|
+
"type": "WORKFLOW_INPUT",
|
1051
|
+
"input_variable_id": str(input_id),
|
1052
|
+
},
|
1053
|
+
"operator": "parseJson",
|
1054
|
+
},
|
1055
|
+
"operator": "=",
|
1056
|
+
"rhs": {
|
1057
|
+
"type": "CONSTANT_VALUE",
|
1058
|
+
"value": {
|
1059
|
+
"type": "JSON",
|
1060
|
+
"value": {"key": "value"},
|
1061
|
+
},
|
1062
|
+
},
|
1063
|
+
},
|
1064
|
+
}
|
1065
|
+
],
|
1066
|
+
"adornments": None,
|
1067
|
+
"attributes": [],
|
1068
|
+
"outputs": [],
|
1069
|
+
},
|
1070
|
+
serialized_node,
|
1071
|
+
ignore_order=True,
|
1072
|
+
)
|
@@ -28,6 +28,7 @@ from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualTo
|
|
28
28
|
from vellum.workflows.expressions.not_between import NotBetweenExpression
|
29
29
|
from vellum.workflows.expressions.not_in import NotInExpression
|
30
30
|
from vellum.workflows.expressions.or_ import OrExpression
|
31
|
+
from vellum.workflows.expressions.parse_json import ParseJsonExpression
|
31
32
|
from vellum.workflows.nodes.bases.base import BaseNode
|
32
33
|
from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
|
33
34
|
from vellum.workflows.references import OutputReference, WorkflowInputReference
|
@@ -163,5 +164,7 @@ def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperato
|
|
163
164
|
return "or"
|
164
165
|
elif isinstance(descriptor, CoalesceExpression):
|
165
166
|
return "coalesce"
|
167
|
+
elif isinstance(descriptor, ParseJsonExpression):
|
168
|
+
return "parseJson"
|
166
169
|
else:
|
167
170
|
raise ValueError(f"Unsupported descriptor type: {descriptor}")
|
@@ -212,8 +212,6 @@ class BaseWorkflowDisplay(
|
|
212
212
|
|
213
213
|
port_displays: Dict[Port, PortDisplay] = {}
|
214
214
|
|
215
|
-
# TODO: We should still serialize nodes that are in the workflow's directory but aren't used in the graph.
|
216
|
-
# https://app.shortcut.com/vellum/story/5394
|
217
215
|
for node in self._workflow.get_nodes():
|
218
216
|
extracted_node_displays = self._extract_node_displays(node)
|
219
217
|
|
@@ -403,11 +401,11 @@ class BaseWorkflowDisplay(
|
|
403
401
|
for input in display_context.workflow_input_displays
|
404
402
|
}
|
405
403
|
node_displays = {
|
406
|
-
|
404
|
+
node.__id__: (node, display_context.node_displays[node]) for node in display_context.node_displays
|
407
405
|
}
|
408
406
|
node_event_displays = {}
|
409
407
|
for node_id in node_displays:
|
410
|
-
current_node_display = node_displays[node_id]
|
408
|
+
node, current_node_display = node_displays[node_id]
|
411
409
|
input_display = {}
|
412
410
|
if isinstance(current_node_display, BaseNodeVellumDisplay):
|
413
411
|
input_display = current_node_display.node_input_ids_by_name
|
@@ -418,7 +416,6 @@ class BaseWorkflowDisplay(
|
|
418
416
|
port_display_meta = {
|
419
417
|
port.name: current_node_display.port_displays[port].id for port in current_node_display.port_displays
|
420
418
|
}
|
421
|
-
node = current_node_display._node
|
422
419
|
subworkflow_display_context: Optional[WorkflowEventDisplayContext] = None
|
423
420
|
if hasattr(node, "subworkflow"):
|
424
421
|
# All nodes that have a subworkflow attribute are currently expected to call them `subworkflow`
|
@@ -432,7 +429,7 @@ class BaseWorkflowDisplay(
|
|
432
429
|
)
|
433
430
|
subworkflow_display_context = subworkflow_display.get_event_display_context()
|
434
431
|
|
435
|
-
node_event_displays[node_id] = NodeEventDisplayContext(
|
432
|
+
node_event_displays[str(node_id)] = NodeEventDisplayContext(
|
436
433
|
input_display=input_display,
|
437
434
|
output_display=output_display,
|
438
435
|
port_display=port_display_meta,
|
@@ -1,9 +1,13 @@
|
|
1
1
|
import pytest
|
2
|
+
from uuid import uuid4
|
2
3
|
|
3
4
|
from vellum.workflows.nodes.bases.base import BaseNode
|
4
5
|
from vellum.workflows.nodes.core.inline_subworkflow_node.node import InlineSubworkflowNode
|
6
|
+
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
7
|
+
from vellum.workflows.nodes.core.templating_node.node import TemplatingNode
|
5
8
|
from vellum.workflows.workflows.base import BaseWorkflow
|
6
9
|
from vellum_ee.workflows.display.nodes import BaseNodeDisplay
|
10
|
+
from vellum_ee.workflows.display.nodes.vellum.retry_node import BaseRetryNodeDisplay
|
7
11
|
from vellum_ee.workflows.display.vellum import NodeDisplayData, NodeDisplayPosition
|
8
12
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
9
13
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
@@ -139,3 +143,51 @@ def test_get_event_display_context__node_display_to_include_subworkflow_display(
|
|
139
143
|
|
140
144
|
assert node_event_display.subworkflow_display is not None
|
141
145
|
assert str(InnerNode.__id__) in node_event_display.subworkflow_display.node_displays
|
146
|
+
|
147
|
+
|
148
|
+
def test_get_event_display_context__node_display_for_adornment_nodes():
|
149
|
+
# GIVEN a simple workflow with a retry node adornment
|
150
|
+
@RetryNode.wrap(max_attempts=4)
|
151
|
+
class MyNode(BaseNode):
|
152
|
+
pass
|
153
|
+
|
154
|
+
class MyWorkflow(BaseWorkflow):
|
155
|
+
graph = MyNode
|
156
|
+
|
157
|
+
# AND a display class for the node
|
158
|
+
inner_node_id = uuid4()
|
159
|
+
|
160
|
+
@BaseRetryNodeDisplay.wrap()
|
161
|
+
class MyNodeDisplay(BaseNodeDisplay[MyNode]):
|
162
|
+
node_id = inner_node_id
|
163
|
+
|
164
|
+
# WHEN we gather the event display context
|
165
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
166
|
+
|
167
|
+
# THEN the subworkflow display should be included
|
168
|
+
assert str(MyNode.__id__) in display_context.node_displays
|
169
|
+
node_event_display = display_context.node_displays[str(MyNode.__id__)]
|
170
|
+
assert node_event_display.subworkflow_display is not None
|
171
|
+
assert str(inner_node_id) in node_event_display.subworkflow_display.node_displays
|
172
|
+
|
173
|
+
|
174
|
+
def test_get_event_display_context__templating_node_input_display():
|
175
|
+
# GIVEN a simple workflow with a templating node referencing another node output
|
176
|
+
class DataNode(BaseNode):
|
177
|
+
class Outputs:
|
178
|
+
bar: str
|
179
|
+
|
180
|
+
class MyNode(TemplatingNode):
|
181
|
+
inputs = {"foo": DataNode.Outputs.bar}
|
182
|
+
|
183
|
+
class MyWorkflow(BaseWorkflow):
|
184
|
+
graph = DataNode >> MyNode
|
185
|
+
|
186
|
+
# WHEN we gather the event display context
|
187
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
188
|
+
|
189
|
+
# THEN the subworkflow display should be included
|
190
|
+
assert str(MyNode.__id__) in display_context.node_displays
|
191
|
+
node_event_display = display_context.node_displays[str(MyNode.__id__)]
|
192
|
+
|
193
|
+
assert node_event_display.input_display.keys() == {"inputs.foo"}
|
File without changes
|
File without changes
|
File without changes
|