vellum-ai 1.7.0__py3-none-any.whl → 1.7.2__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 +2 -2
- vellum/workflows/integrations/tests/test_vellum_integration_service.py +14 -12
- vellum/workflows/integrations/vellum_integration_service.py +9 -9
- vellum/workflows/nodes/displayable/code_execution_node/node.py +19 -7
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +101 -0
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +2 -2
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +14 -5
- vellum/workflows/resolvers/resolver.py +13 -46
- vellum/workflows/resolvers/tests/test_resolver.py +27 -112
- vellum/workflows/state/base.py +14 -4
- vellum/workflows/types/core.py +0 -24
- vellum/workflows/types/definition.py +15 -1
- {vellum_ai-1.7.0.dist-info → vellum_ai-1.7.2.dist-info}/METADATA +1 -1
- {vellum_ai-1.7.0.dist-info → vellum_ai-1.7.2.dist-info}/RECORD +21 -21
- vellum_ee/assets/node-definitions.json +46 -6
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +30 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +9 -1
- {vellum_ai-1.7.0.dist-info → vellum_ai-1.7.2.dist-info}/LICENSE +0 -0
- {vellum_ai-1.7.0.dist-info → vellum_ai-1.7.2.dist-info}/WHEEL +0 -0
- {vellum_ai-1.7.0.dist-info → vellum_ai-1.7.2.dist-info}/entry_points.txt +0 -0
@@ -27,10 +27,10 @@ class BaseClientWrapper:
|
|
27
27
|
|
28
28
|
def get_headers(self) -> typing.Dict[str, str]:
|
29
29
|
headers: typing.Dict[str, str] = {
|
30
|
-
"User-Agent": "vellum-ai/1.7.
|
30
|
+
"User-Agent": "vellum-ai/1.7.2",
|
31
31
|
"X-Fern-Language": "Python",
|
32
32
|
"X-Fern-SDK-Name": "vellum-ai",
|
33
|
-
"X-Fern-SDK-Version": "1.7.
|
33
|
+
"X-Fern-SDK-Version": "1.7.2",
|
34
34
|
**(self.get_custom_headers() or {}),
|
35
35
|
}
|
36
36
|
if self._api_version is not None:
|
@@ -12,6 +12,7 @@ from vellum.workflows.types.definition import VellumIntegrationToolDetails
|
|
12
12
|
|
13
13
|
def test_vellum_integration_service_get_tool_definition_success(vellum_client):
|
14
14
|
"""Test that tool definitions are successfully retrieved from Vellum API"""
|
15
|
+
# GIVEN a mock client configured to return a tool definition
|
15
16
|
mock_client = vellum_client
|
16
17
|
tool_definition_response = ComponentsSchemasComposioToolDefinition(
|
17
18
|
integration=ToolDefinitionIntegration(
|
@@ -33,7 +34,6 @@ def test_vellum_integration_service_get_tool_definition_success(vellum_client):
|
|
33
34
|
},
|
34
35
|
output_parameters={},
|
35
36
|
)
|
36
|
-
|
37
37
|
mock_client.integrations.retrieve_integration_tool_definition.return_value = tool_definition_response
|
38
38
|
|
39
39
|
# WHEN we request a tool definition
|
@@ -66,6 +66,7 @@ def test_vellum_integration_service_get_tool_definition_success(vellum_client):
|
|
66
66
|
|
67
67
|
def test_vellum_integration_service_get_tool_definition_api_error(vellum_client):
|
68
68
|
"""Test that API errors are properly handled when retrieving tool definitions"""
|
69
|
+
# GIVEN a mock client configured to raise an exception
|
69
70
|
mock_client = vellum_client
|
70
71
|
mock_client.integrations = mock.MagicMock()
|
71
72
|
mock_client.integrations.retrieve_integration_tool_definition.side_effect = Exception("Tool not found")
|
@@ -87,6 +88,7 @@ def test_vellum_integration_service_get_tool_definition_api_error(vellum_client)
|
|
87
88
|
|
88
89
|
def test_vellum_integration_service_execute_tool_success(vellum_client):
|
89
90
|
"""Test that tools are successfully executed via Vellum API"""
|
91
|
+
# GIVEN a mock client configured to return a successful response
|
90
92
|
mock_client = vellum_client
|
91
93
|
mock_client.integrations = mock.MagicMock()
|
92
94
|
|
@@ -96,7 +98,6 @@ def test_vellum_integration_service_execute_tool_success(vellum_client):
|
|
96
98
|
"issue_id": 123,
|
97
99
|
"issue_url": "https://github.com/user/repo/issues/123",
|
98
100
|
}
|
99
|
-
|
100
101
|
mock_client.integrations.execute_integration_tool.return_value = mock_response
|
101
102
|
|
102
103
|
# WHEN we execute a tool with valid arguments
|
@@ -132,6 +133,7 @@ def test_vellum_integration_service_execute_tool_success(vellum_client):
|
|
132
133
|
|
133
134
|
def test_vellum_integration_service_execute_tool_api_error(vellum_client):
|
134
135
|
"""Test that execution errors are properly handled"""
|
136
|
+
# GIVEN a mock client configured to raise an exception
|
135
137
|
mock_client = vellum_client
|
136
138
|
mock_client.integrations = mock.MagicMock()
|
137
139
|
mock_client.integrations.execute_integration_tool.side_effect = Exception("Authentication failed")
|
@@ -154,12 +156,12 @@ def test_vellum_integration_service_execute_tool_api_error(vellum_client):
|
|
154
156
|
|
155
157
|
def test_vellum_integration_service_execute_tool_empty_response(vellum_client):
|
156
158
|
"""Test that empty response data is handled gracefully"""
|
159
|
+
# GIVEN a mock client configured to return an empty response
|
157
160
|
mock_client = vellum_client
|
158
161
|
mock_client.integrations = mock.MagicMock()
|
159
162
|
|
160
163
|
mock_response = mock.MagicMock()
|
161
164
|
mock_response.data = {}
|
162
|
-
|
163
165
|
mock_client.integrations.execute_integration_tool.return_value = mock_response
|
164
166
|
|
165
167
|
# WHEN we execute a tool that returns empty data
|
@@ -180,6 +182,7 @@ def test_vellum_integration_service_execute_tool_empty_response(vellum_client):
|
|
180
182
|
|
181
183
|
def test_vellum_integration_service_multiple_tool_executions(vellum_client):
|
182
184
|
"""Test that the service handles multiple sequential tool executions"""
|
185
|
+
# GIVEN a mock client configured to return different responses for multiple calls
|
183
186
|
mock_client = vellum_client
|
184
187
|
mock_client.integrations = mock.MagicMock()
|
185
188
|
|
@@ -214,16 +217,18 @@ def test_vellum_integration_service_multiple_tool_executions(vellum_client):
|
|
214
217
|
assert mock_client.integrations.execute_integration_tool.call_count == 2
|
215
218
|
|
216
219
|
|
217
|
-
def
|
218
|
-
"""Test
|
220
|
+
def test_vellum_integration_service_execute_tool_structured_403_with_integration(vellum_client):
|
221
|
+
"""Test structured 403 responses with integration field (current backend format)"""
|
219
222
|
from vellum.client.core.api_error import ApiError
|
220
223
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
221
224
|
|
225
|
+
# GIVEN a mock client configured to raise a structured 403 error with integration
|
222
226
|
mock_client = vellum_client
|
223
227
|
mock_client.integrations = mock.MagicMock()
|
224
228
|
|
225
|
-
# Mock
|
229
|
+
# Mock current backend structure with integration as direct field
|
226
230
|
structured_error_body = {
|
231
|
+
"code": "INTEGRATION_CREDENTIALS_UNAVAILABLE",
|
227
232
|
"message": "You must authenticate with this integration before you can execute this tool.",
|
228
233
|
"integration": {
|
229
234
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
@@ -231,15 +236,13 @@ def test_vellum_integration_service_execute_tool_structured_403_error(vellum_cli
|
|
231
236
|
"name": "GITHUB",
|
232
237
|
},
|
233
238
|
}
|
234
|
-
|
235
239
|
mock_client.integrations.execute_integration_tool.side_effect = ApiError(
|
236
240
|
status_code=403,
|
237
241
|
body=structured_error_body,
|
238
242
|
)
|
239
243
|
|
240
|
-
service = VellumIntegrationService(client=mock_client)
|
241
|
-
|
242
244
|
# WHEN we attempt to execute a tool without credentials
|
245
|
+
service = VellumIntegrationService(client=mock_client)
|
243
246
|
with pytest.raises(NodeException) as exc_info:
|
244
247
|
service.execute_tool(
|
245
248
|
integration="GITHUB",
|
@@ -266,19 +269,18 @@ def test_vellum_integration_service_execute_tool_legacy_403_error(vellum_client)
|
|
266
269
|
from vellum.client.core.api_error import ApiError
|
267
270
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
268
271
|
|
272
|
+
# GIVEN a mock client configured to raise a legacy 403 error
|
269
273
|
mock_client = vellum_client
|
270
274
|
mock_client.integrations = mock.MagicMock()
|
271
275
|
|
272
|
-
# Mock legacy 403 response format (just detail field)
|
273
276
|
legacy_error_body = {"detail": "You do not have permission to execute this tool."}
|
274
|
-
|
275
277
|
mock_client.integrations.execute_integration_tool.side_effect = ApiError(
|
276
278
|
status_code=403,
|
277
279
|
body=legacy_error_body,
|
278
280
|
)
|
279
281
|
|
282
|
+
# WHEN we attempt to execute a tool that returns a legacy 403 error
|
280
283
|
service = VellumIntegrationService(client=mock_client)
|
281
|
-
|
282
284
|
with pytest.raises(NodeException) as exc_info:
|
283
285
|
service.execute_tool(
|
284
286
|
integration="GITHUB",
|
@@ -95,15 +95,15 @@ class VellumIntegrationService:
|
|
95
95
|
except ApiError as e:
|
96
96
|
# Handle structured 403 credential error responses
|
97
97
|
if e.status_code == 403 and isinstance(e.body, dict):
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
}
|
98
|
+
# Check for backend structure with integration as direct field
|
99
|
+
integration_from_backend = e.body.get("integration")
|
100
|
+
if integration_from_backend:
|
101
|
+
error_message = e.body.get(
|
102
|
+
"message", "You must authenticate with this integration before you can execute this tool."
|
103
|
+
)
|
104
|
+
|
105
|
+
# Wrap integration in raw_data for frontend consumption
|
106
|
+
raw_data = {"integration": integration_from_backend}
|
107
107
|
|
108
108
|
raise NodeException(
|
109
109
|
message=error_message,
|
@@ -117,13 +117,7 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
|
|
117
117
|
request_options=self.request_options,
|
118
118
|
)
|
119
119
|
except ApiError as e:
|
120
|
-
|
121
|
-
raise NodeException(
|
122
|
-
message=e.body["message"],
|
123
|
-
code=WorkflowErrorCode.INVALID_INPUTS,
|
124
|
-
)
|
125
|
-
|
126
|
-
raise
|
120
|
+
self._handle_api_error(e)
|
127
121
|
|
128
122
|
if code_execution_result.output.type != expected_output_type:
|
129
123
|
actual_type = code_execution_result.output.type
|
@@ -134,6 +128,24 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
|
|
134
128
|
|
135
129
|
return self.Outputs(result=code_execution_result.output.value, log=code_execution_result.log)
|
136
130
|
|
131
|
+
def _handle_api_error(self, e: ApiError) -> None:
|
132
|
+
if e.status_code and e.status_code == 403 and isinstance(e.body, dict):
|
133
|
+
raise NodeException(
|
134
|
+
message=e.body.get("detail", "Provider credentials is missing or unavailable"),
|
135
|
+
code=WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE,
|
136
|
+
) from e
|
137
|
+
|
138
|
+
if e.status_code and e.status_code >= 400 and e.status_code < 500 and isinstance(e.body, dict):
|
139
|
+
raise NodeException(
|
140
|
+
message=e.body.get("message", e.body.get("detail", "Failed to execute code")),
|
141
|
+
code=WorkflowErrorCode.INVALID_INPUTS,
|
142
|
+
) from e
|
143
|
+
|
144
|
+
raise NodeException(
|
145
|
+
message="Failed to execute code",
|
146
|
+
code=WorkflowErrorCode.INTERNAL_ERROR,
|
147
|
+
) from e
|
148
|
+
|
137
149
|
def _has_secrets_in_code_inputs(self) -> bool:
|
138
150
|
"""Check if any code_inputs contain VellumSecret instances that require API execution."""
|
139
151
|
for input_value in self.code_inputs.values():
|
@@ -6,7 +6,11 @@ from typing import Any, List, Union
|
|
6
6
|
from pydantic import BaseModel
|
7
7
|
|
8
8
|
from vellum import ArrayInput, CodeExecutorResponse, NumberVellumValue, StringInput, StringVellumValue
|
9
|
+
from vellum.client.core.api_error import ApiError
|
9
10
|
from vellum.client.errors.bad_request_error import BadRequestError
|
11
|
+
from vellum.client.errors.forbidden_error import ForbiddenError
|
12
|
+
from vellum.client.errors.internal_server_error import InternalServerError
|
13
|
+
from vellum.client.errors.not_found_error import NotFoundError
|
10
14
|
from vellum.client.types.chat_message import ChatMessage
|
11
15
|
from vellum.client.types.code_execution_package import CodeExecutionPackage
|
12
16
|
from vellum.client.types.code_executor_secret_input import CodeExecutorSecretInput
|
@@ -792,6 +796,103 @@ Node.js v21.7.3
|
|
792
796
|
assert exc_info.value.message == message
|
793
797
|
|
794
798
|
|
799
|
+
def test_run_node__execute_code_api_fails_403__provider_credentials_unavailable(vellum_client):
|
800
|
+
"""
|
801
|
+
Tests that a 403 error from the API is handled with PROVIDER_CREDENTIALS_UNAVAILABLE error code.
|
802
|
+
"""
|
803
|
+
|
804
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
|
805
|
+
code = "def main(): return 'test'"
|
806
|
+
runtime = "PYTHON_3_11_6"
|
807
|
+
packages = [CodeExecutionPackage(name="requests", version="2.28.0")]
|
808
|
+
|
809
|
+
vellum_client.execute_code.side_effect = ForbiddenError(
|
810
|
+
body={
|
811
|
+
"detail": "Provider credentials is missing or unavailable",
|
812
|
+
}
|
813
|
+
)
|
814
|
+
|
815
|
+
node = ExampleCodeExecutionNode()
|
816
|
+
with pytest.raises(NodeException) as exc_info:
|
817
|
+
node.run()
|
818
|
+
|
819
|
+
assert exc_info.value.code == WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE
|
820
|
+
assert "Provider credentials is missing or unavailable" in exc_info.value.message
|
821
|
+
|
822
|
+
|
823
|
+
def test_run_node__execute_code_api_fails_404__invalid_inputs(vellum_client):
|
824
|
+
"""
|
825
|
+
Tests that a 404 error from the API is handled with INVALID_INPUTS error code.
|
826
|
+
"""
|
827
|
+
|
828
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
|
829
|
+
code = "def main(): return 'test'"
|
830
|
+
runtime = "PYTHON_3_11_6"
|
831
|
+
packages = [CodeExecutionPackage(name="requests", version="2.28.0")]
|
832
|
+
|
833
|
+
vellum_client.execute_code.side_effect = NotFoundError(
|
834
|
+
body={
|
835
|
+
"detail": "Resource not found",
|
836
|
+
}
|
837
|
+
)
|
838
|
+
|
839
|
+
node = ExampleCodeExecutionNode()
|
840
|
+
with pytest.raises(NodeException) as exc_info:
|
841
|
+
node.run()
|
842
|
+
|
843
|
+
assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
|
844
|
+
assert "Resource not found" in exc_info.value.message
|
845
|
+
|
846
|
+
|
847
|
+
def test_run_node__execute_code_api_fails_500__internal_error(vellum_client):
|
848
|
+
"""
|
849
|
+
Tests that a 500 error from the API is handled with INTERNAL_ERROR error code.
|
850
|
+
"""
|
851
|
+
|
852
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
|
853
|
+
code = "def main(): return 'test'"
|
854
|
+
runtime = "PYTHON_3_11_6"
|
855
|
+
packages = [CodeExecutionPackage(name="requests", version="2.28.0")]
|
856
|
+
|
857
|
+
vellum_client.execute_code.side_effect = InternalServerError(
|
858
|
+
body={
|
859
|
+
"detail": "Internal server error occurred",
|
860
|
+
}
|
861
|
+
)
|
862
|
+
|
863
|
+
node = ExampleCodeExecutionNode()
|
864
|
+
with pytest.raises(NodeException) as exc_info:
|
865
|
+
node.run()
|
866
|
+
|
867
|
+
assert exc_info.value.code == WorkflowErrorCode.INTERNAL_ERROR
|
868
|
+
assert exc_info.value.message == "Failed to execute code"
|
869
|
+
|
870
|
+
|
871
|
+
def test_run_node__execute_code_api_fails_4xx_no_message__uses_detail(vellum_client):
|
872
|
+
"""
|
873
|
+
Tests that a 4xx error without a 'message' field falls back to 'detail' field.
|
874
|
+
"""
|
875
|
+
|
876
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
|
877
|
+
code = "def main(): return 'test'"
|
878
|
+
runtime = "PYTHON_3_11_6"
|
879
|
+
packages = [CodeExecutionPackage(name="requests", version="2.28.0")]
|
880
|
+
|
881
|
+
vellum_client.execute_code.side_effect = ApiError(
|
882
|
+
status_code=422,
|
883
|
+
body={
|
884
|
+
"detail": "Invalid request parameters",
|
885
|
+
},
|
886
|
+
)
|
887
|
+
|
888
|
+
node = ExampleCodeExecutionNode()
|
889
|
+
with pytest.raises(NodeException) as exc_info:
|
890
|
+
node.run()
|
891
|
+
|
892
|
+
assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
|
893
|
+
assert "Invalid request parameters" in exc_info.value.message
|
894
|
+
|
895
|
+
|
795
896
|
def test_run_node__execute_code__list_extends():
|
796
897
|
# GIVEN a node that will return a list with output type Json
|
797
898
|
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Json]):
|
@@ -22,8 +22,8 @@ from vellum.workflows.nodes.displayable.tool_calling_node.utils import (
|
|
22
22
|
)
|
23
23
|
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
24
24
|
from vellum.workflows.state.context import WorkflowContext
|
25
|
-
from vellum.workflows.types.core import EntityInputsInterface
|
26
|
-
from vellum.workflows.types.definition import MCPServer
|
25
|
+
from vellum.workflows.types.core import EntityInputsInterface
|
26
|
+
from vellum.workflows.types.definition import MCPServer, Tool
|
27
27
|
from vellum.workflows.types.generics import StateType
|
28
28
|
from vellum.workflows.utils.functions import compile_mcp_tool_definition, get_mcp_tool_name
|
29
29
|
from vellum.workflows.workflows.event_filters import all_workflow_event_filter
|
@@ -31,12 +31,14 @@ from vellum.workflows.outputs.base import BaseOutput
|
|
31
31
|
from vellum.workflows.ports.port import Port
|
32
32
|
from vellum.workflows.state import BaseState
|
33
33
|
from vellum.workflows.state.encoder import DefaultStateEncoder
|
34
|
-
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
|
34
|
+
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
|
35
35
|
from vellum.workflows.types.definition import (
|
36
36
|
ComposioToolDefinition,
|
37
37
|
DeploymentDefinition,
|
38
38
|
MCPServer,
|
39
39
|
MCPToolDefinition,
|
40
|
+
Tool,
|
41
|
+
ToolBase,
|
40
42
|
VellumIntegrationToolDefinition,
|
41
43
|
)
|
42
44
|
from vellum.workflows.types.generics import is_workflow_class
|
@@ -203,7 +205,7 @@ class FunctionNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
203
205
|
raise NodeException(
|
204
206
|
message=f"Error executing function '{function_name}': {str(e)}",
|
205
207
|
code=WorkflowErrorCode.NODE_EXECUTION,
|
206
|
-
)
|
208
|
+
) from e
|
207
209
|
|
208
210
|
# Add the result to the chat history
|
209
211
|
self._add_function_result_to_chat_history(result, self.state)
|
@@ -233,7 +235,7 @@ class ComposioNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
233
235
|
raise NodeException(
|
234
236
|
message=f"Error executing Composio tool '{self.composio_tool.action}': {str(e)}",
|
235
237
|
code=WorkflowErrorCode.NODE_EXECUTION,
|
236
|
-
)
|
238
|
+
) from e
|
237
239
|
|
238
240
|
# Add result to chat history
|
239
241
|
self._add_function_result_to_chat_history(result, self.state)
|
@@ -256,7 +258,7 @@ class MCPNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
256
258
|
raise NodeException(
|
257
259
|
message=f"Error executing MCP tool '{self.mcp_tool.name}': {str(e)}",
|
258
260
|
code=WorkflowErrorCode.NODE_EXECUTION,
|
259
|
-
)
|
261
|
+
) from e
|
260
262
|
|
261
263
|
# Add result to chat history
|
262
264
|
self._add_function_result_to_chat_history(result, self.state)
|
@@ -281,11 +283,18 @@ class VellumIntegrationNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
281
283
|
tool_name=self.vellum_integration_tool.name,
|
282
284
|
arguments=arguments,
|
283
285
|
)
|
286
|
+
except NodeException as e:
|
287
|
+
# Preserve original error code and raw_data while adding context
|
288
|
+
raise NodeException(
|
289
|
+
message=f"Error executing Vellum Integration tool '{self.vellum_integration_tool.name}': {e.message}",
|
290
|
+
code=e.code,
|
291
|
+
raw_data=e.raw_data,
|
292
|
+
) from e
|
284
293
|
except Exception as e:
|
285
294
|
raise NodeException(
|
286
295
|
message=f"Error executing Vellum Integration tool '{self.vellum_integration_tool.name}': {str(e)}",
|
287
296
|
code=WorkflowErrorCode.NODE_EXECUTION,
|
288
|
-
)
|
297
|
+
) from e
|
289
298
|
|
290
299
|
# Add result to chat history
|
291
300
|
self._add_function_result_to_chat_history(result, self.state)
|
@@ -1,9 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
from uuid import UUID
|
3
|
-
from typing import Iterator,
|
3
|
+
from typing import Iterator, Optional, Type, Union
|
4
4
|
|
5
|
-
from vellum.client.types.vellum_span import VellumSpan
|
6
|
-
from vellum.client.types.workflow_execution_initiated_event import WorkflowExecutionInitiatedEvent
|
7
5
|
from vellum.workflows.events.workflow import WorkflowEvent
|
8
6
|
from vellum.workflows.nodes.utils import cast_to_output_type
|
9
7
|
from vellum.workflows.resolvers.base import BaseWorkflowResolver
|
@@ -20,38 +18,6 @@ class VellumResolver(BaseWorkflowResolver):
|
|
20
18
|
def get_state_snapshot_history(self) -> Iterator[BaseState]:
|
21
19
|
return iter([])
|
22
20
|
|
23
|
-
def _find_previous_and_root_span(
|
24
|
-
self, execution_id: str, spans: List[VellumSpan]
|
25
|
-
) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str]]:
|
26
|
-
previous_trace_id: Optional[str] = None
|
27
|
-
root_trace_id: Optional[str] = None
|
28
|
-
previous_span_id: Optional[str] = None
|
29
|
-
root_span_id: Optional[str] = None
|
30
|
-
|
31
|
-
for span in spans:
|
32
|
-
# Look for workflow execution spans with matching ID first
|
33
|
-
if span.name == "workflow.execution" and span.span_id == execution_id:
|
34
|
-
# Find the WorkflowExecutionInitiatedEvent in the span's events
|
35
|
-
initiated_event = next(
|
36
|
-
(event for event in span.events if isinstance(event, WorkflowExecutionInitiatedEvent)), None
|
37
|
-
)
|
38
|
-
if initiated_event:
|
39
|
-
previous_trace_id = initiated_event.trace_id
|
40
|
-
previous_span_id = initiated_event.span_id
|
41
|
-
links = initiated_event.links
|
42
|
-
if links:
|
43
|
-
root_span = next((link for link in links if link.type == "ROOT_SPAN"), None)
|
44
|
-
if root_span:
|
45
|
-
root_trace_id = root_span.trace_id
|
46
|
-
root_span_id = root_span.span_context.span_id
|
47
|
-
else:
|
48
|
-
# no links means this is the first execution
|
49
|
-
root_trace_id = initiated_event.trace_id
|
50
|
-
root_span_id = initiated_event.span_id
|
51
|
-
break
|
52
|
-
|
53
|
-
return previous_trace_id, root_trace_id, previous_span_id, root_span_id
|
54
|
-
|
55
21
|
def _deserialize_state(self, state_data: dict, state_class: Type[BaseState]) -> BaseState:
|
56
22
|
"""Deserialize state data with proper type conversion for complex types like List[ChatMessage]."""
|
57
23
|
converted_data = {}
|
@@ -79,18 +45,19 @@ class VellumResolver(BaseWorkflowResolver):
|
|
79
45
|
return None
|
80
46
|
|
81
47
|
client = self._context.vellum_client
|
82
|
-
response = client.
|
83
|
-
|
48
|
+
response = client.workflows.retrieve_state(
|
49
|
+
span_id=previous_execution_id,
|
84
50
|
)
|
85
51
|
|
86
52
|
if response.state is None:
|
87
53
|
return None
|
88
54
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
55
|
+
if (
|
56
|
+
response.previous_trace_id is None
|
57
|
+
or response.root_trace_id is None
|
58
|
+
or response.previous_span_id is None
|
59
|
+
or response.root_span_id is None
|
60
|
+
):
|
94
61
|
logger.warning("Could not find required execution events for state loading")
|
95
62
|
return None
|
96
63
|
|
@@ -106,8 +73,8 @@ class VellumResolver(BaseWorkflowResolver):
|
|
106
73
|
|
107
74
|
return LoadStateResult(
|
108
75
|
state=state,
|
109
|
-
previous_trace_id=previous_trace_id,
|
110
|
-
previous_span_id=previous_span_id,
|
111
|
-
root_trace_id=root_trace_id,
|
112
|
-
root_span_id=root_span_id,
|
76
|
+
previous_trace_id=response.previous_trace_id,
|
77
|
+
previous_span_id=response.previous_span_id,
|
78
|
+
root_trace_id=response.root_trace_id,
|
79
|
+
root_span_id=response.root_span_id,
|
113
80
|
)
|
@@ -4,14 +4,7 @@ from uuid import uuid4
|
|
4
4
|
from typing import List
|
5
5
|
|
6
6
|
from vellum import ChatMessage
|
7
|
-
from vellum.client.types.
|
8
|
-
from vellum.client.types.vellum_code_resource_definition import VellumCodeResourceDefinition
|
9
|
-
from vellum.client.types.workflow_execution_detail import WorkflowExecutionDetail
|
10
|
-
from vellum.client.types.workflow_execution_initiated_body import WorkflowExecutionInitiatedBody
|
11
|
-
from vellum.client.types.workflow_execution_initiated_event import WorkflowExecutionInitiatedEvent
|
12
|
-
from vellum.client.types.workflow_execution_span import WorkflowExecutionSpan
|
13
|
-
from vellum.client.types.workflow_execution_span_attributes import WorkflowExecutionSpanAttributes
|
14
|
-
from vellum.client.types.workflow_parent_context import WorkflowParentContext
|
7
|
+
from vellum.client.types.workflow_resolved_state import WorkflowResolvedState
|
15
8
|
from vellum.workflows import BaseWorkflow
|
16
9
|
from vellum.workflows.inputs.base import BaseInputs
|
17
10
|
from vellum.workflows.resolvers.resolver import VellumResolver
|
@@ -24,7 +17,6 @@ def test_load_state_with_context_success():
|
|
24
17
|
"""Test load_state successfully loads state when context and client are available."""
|
25
18
|
resolver = VellumResolver()
|
26
19
|
execution_id = uuid4()
|
27
|
-
root_execution_id = uuid4()
|
28
20
|
|
29
21
|
class TestState(BaseState):
|
30
22
|
test_key: str = "test_value"
|
@@ -50,60 +42,24 @@ def test_load_state_with_context_success():
|
|
50
42
|
},
|
51
43
|
}
|
52
44
|
|
53
|
-
mock_workflow_definition = VellumCodeResourceDefinition(
|
54
|
-
name="TestWorkflow", module=["test", "module"], id=str(uuid4())
|
55
|
-
)
|
56
|
-
|
57
|
-
mock_body = WorkflowExecutionInitiatedBody(workflow_definition=mock_workflow_definition, inputs={})
|
58
|
-
|
59
45
|
previous_trace_id = str(uuid4())
|
46
|
+
previous_span_id = str(uuid4())
|
60
47
|
root_trace_id = str(uuid4())
|
48
|
+
root_span_id = str(uuid4())
|
61
49
|
|
62
|
-
|
63
|
-
|
50
|
+
mock_response = WorkflowResolvedState(
|
51
|
+
trace_id=str(uuid4()),
|
64
52
|
timestamp=datetime.now(),
|
65
|
-
trace_id=previous_trace_id,
|
66
53
|
span_id=str(execution_id),
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
span_context=WorkflowParentContext(workflow_definition=mock_workflow_definition, span_id=str(uuid4())),
|
73
|
-
),
|
74
|
-
SpanLink(
|
75
|
-
trace_id=root_trace_id,
|
76
|
-
type="ROOT_SPAN",
|
77
|
-
span_context=WorkflowParentContext(
|
78
|
-
workflow_definition=mock_workflow_definition, span_id=str(root_execution_id)
|
79
|
-
),
|
80
|
-
),
|
81
|
-
],
|
82
|
-
)
|
83
|
-
|
84
|
-
root_invocation = WorkflowExecutionInitiatedEvent(
|
85
|
-
id=str(uuid4()),
|
86
|
-
timestamp=datetime.now(),
|
87
|
-
trace_id=root_trace_id,
|
88
|
-
span_id=str(root_execution_id),
|
89
|
-
body=mock_body,
|
90
|
-
links=None, # Root invocation has no links
|
91
|
-
)
|
92
|
-
|
93
|
-
mock_span = WorkflowExecutionSpan(
|
94
|
-
span_id=str(execution_id), # Use the actual execution_id
|
95
|
-
start_ts=datetime.now(),
|
96
|
-
end_ts=datetime.now(),
|
97
|
-
attributes=WorkflowExecutionSpanAttributes(label="Test Workflow", workflow_id=str(uuid4())),
|
98
|
-
events=[previous_invocation, root_invocation],
|
99
|
-
)
|
100
|
-
|
101
|
-
mock_response = WorkflowExecutionDetail(
|
102
|
-
span_id="test-span-id", start=datetime.now(), inputs=[], outputs=[], spans=[mock_span], state=state_dict
|
54
|
+
state=state_dict,
|
55
|
+
previous_trace_id=previous_trace_id,
|
56
|
+
previous_span_id=previous_span_id,
|
57
|
+
root_trace_id=root_trace_id,
|
58
|
+
root_span_id=root_span_id,
|
103
59
|
)
|
104
60
|
|
105
61
|
mock_client = Mock()
|
106
|
-
mock_client.
|
62
|
+
mock_client.workflows.retrieve_state.return_value = mock_response
|
107
63
|
|
108
64
|
# AND context with the test workflow class is set up
|
109
65
|
context = WorkflowContext(vellum_client=mock_client)
|
@@ -123,21 +79,18 @@ def test_load_state_with_context_success():
|
|
123
79
|
assert str(result.state.meta.span_id) != prev_span_id
|
124
80
|
|
125
81
|
# AND should have span link info
|
126
|
-
assert result.previous_trace_id ==
|
127
|
-
assert result.previous_span_id ==
|
128
|
-
assert result.root_trace_id ==
|
129
|
-
assert result.root_span_id ==
|
82
|
+
assert result.previous_trace_id == previous_trace_id
|
83
|
+
assert result.previous_span_id == previous_span_id
|
84
|
+
assert result.root_trace_id == root_trace_id
|
85
|
+
assert result.root_span_id == root_span_id
|
130
86
|
|
131
|
-
mock_client.
|
132
|
-
execution_id=str(execution_id)
|
133
|
-
)
|
87
|
+
mock_client.workflows.retrieve_state.assert_called_once_with(span_id=str(execution_id))
|
134
88
|
|
135
89
|
|
136
90
|
def test_load_state_with_chat_message_list():
|
137
91
|
"""Test load_state successfully loads state with chat_history containing ChatMessage list."""
|
138
92
|
resolver = VellumResolver()
|
139
93
|
execution_id = uuid4()
|
140
|
-
root_execution_id = uuid4()
|
141
94
|
|
142
95
|
class TestStateWithChatHistory(BaseState):
|
143
96
|
test_key: str = "test_value"
|
@@ -169,60 +122,24 @@ def test_load_state_with_chat_message_list():
|
|
169
122
|
},
|
170
123
|
}
|
171
124
|
|
172
|
-
mock_workflow_definition = VellumCodeResourceDefinition(
|
173
|
-
name="TestWorkflow", module=["test", "module"], id=str(uuid4())
|
174
|
-
)
|
175
|
-
|
176
|
-
mock_body = WorkflowExecutionInitiatedBody(workflow_definition=mock_workflow_definition, inputs={})
|
177
|
-
|
178
125
|
previous_trace_id = str(uuid4())
|
126
|
+
previous_span_id = str(uuid4())
|
179
127
|
root_trace_id = str(uuid4())
|
128
|
+
root_span_id = str(uuid4())
|
180
129
|
|
181
|
-
|
182
|
-
|
183
|
-
timestamp=datetime.now(),
|
184
|
-
trace_id=previous_trace_id,
|
185
|
-
span_id=str(execution_id),
|
186
|
-
body=mock_body,
|
187
|
-
links=[
|
188
|
-
SpanLink(
|
189
|
-
trace_id=previous_trace_id,
|
190
|
-
type="PREVIOUS_SPAN",
|
191
|
-
span_context=WorkflowParentContext(workflow_definition=mock_workflow_definition, span_id=str(uuid4())),
|
192
|
-
),
|
193
|
-
SpanLink(
|
194
|
-
trace_id=root_trace_id,
|
195
|
-
type="ROOT_SPAN",
|
196
|
-
span_context=WorkflowParentContext(
|
197
|
-
workflow_definition=mock_workflow_definition, span_id=str(root_execution_id)
|
198
|
-
),
|
199
|
-
),
|
200
|
-
],
|
201
|
-
)
|
202
|
-
|
203
|
-
root_invocation = WorkflowExecutionInitiatedEvent(
|
204
|
-
id=str(uuid4()),
|
130
|
+
mock_response = WorkflowResolvedState(
|
131
|
+
trace_id=str(uuid4()),
|
205
132
|
timestamp=datetime.now(),
|
206
|
-
trace_id=root_trace_id,
|
207
|
-
span_id=str(root_execution_id),
|
208
|
-
body=mock_body,
|
209
|
-
links=None,
|
210
|
-
)
|
211
|
-
|
212
|
-
mock_span = WorkflowExecutionSpan(
|
213
133
|
span_id=str(execution_id),
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
mock_response = WorkflowExecutionDetail(
|
221
|
-
span_id="test-span-id", start=datetime.now(), inputs=[], outputs=[], spans=[mock_span], state=state_dict
|
134
|
+
state=state_dict,
|
135
|
+
previous_trace_id=previous_trace_id,
|
136
|
+
previous_span_id=previous_span_id,
|
137
|
+
root_trace_id=root_trace_id,
|
138
|
+
root_span_id=root_span_id,
|
222
139
|
)
|
223
140
|
|
224
141
|
mock_client = Mock()
|
225
|
-
mock_client.
|
142
|
+
mock_client.workflows.retrieve_state.return_value = mock_response
|
226
143
|
|
227
144
|
# AND context with the test workflow class is set up
|
228
145
|
context = WorkflowContext(vellum_client=mock_client)
|
@@ -247,6 +164,4 @@ def test_load_state_with_chat_message_list():
|
|
247
164
|
assert result.state.chat_history[2].role == "USER"
|
248
165
|
assert result.state.chat_history[2].text == "What can you help me with?"
|
249
166
|
|
250
|
-
mock_client.
|
251
|
-
execution_id=str(execution_id)
|
252
|
-
)
|
167
|
+
mock_client.workflows.retrieve_state.assert_called_once_with(span_id=str(execution_id))
|
vellum/workflows/state/base.py
CHANGED
@@ -442,14 +442,24 @@ class StateMeta(UniversalBaseModel):
|
|
442
442
|
if not memo:
|
443
443
|
memo = {}
|
444
444
|
|
445
|
+
node_output_keys = list(self.node_outputs.keys())
|
445
446
|
new_node_outputs = {
|
446
|
-
descriptor:
|
447
|
-
|
447
|
+
descriptor: (
|
448
|
+
self.node_outputs[descriptor]
|
449
|
+
if isinstance(self.node_outputs[descriptor], Queue)
|
450
|
+
else deepcopy(self.node_outputs[descriptor], memo)
|
451
|
+
)
|
452
|
+
for descriptor in node_output_keys
|
448
453
|
}
|
449
454
|
|
455
|
+
external_input_keys = list(self.external_inputs.keys())
|
450
456
|
new_external_inputs = {
|
451
|
-
descriptor:
|
452
|
-
|
457
|
+
descriptor: (
|
458
|
+
self.external_inputs[descriptor]
|
459
|
+
if isinstance(self.external_inputs[descriptor], Queue)
|
460
|
+
else deepcopy(self.external_inputs[descriptor], memo)
|
461
|
+
)
|
462
|
+
for descriptor in external_input_keys
|
453
463
|
}
|
454
464
|
|
455
465
|
memo[id(self.node_outputs)] = new_node_outputs
|
vellum/workflows/types/core.py
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import ( # type: ignore[attr-defined]
|
3
|
-
TYPE_CHECKING,
|
4
3
|
Any,
|
5
|
-
Callable,
|
6
4
|
Dict,
|
7
5
|
List,
|
8
|
-
Type,
|
9
6
|
Union,
|
10
7
|
_GenericAlias,
|
11
8
|
_SpecialGenericAlias,
|
@@ -13,16 +10,6 @@ from typing import ( # type: ignore[attr-defined]
|
|
13
10
|
)
|
14
11
|
|
15
12
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
16
|
-
from vellum.workflows.types.definition import (
|
17
|
-
ComposioToolDefinition,
|
18
|
-
DeploymentDefinition,
|
19
|
-
MCPServer,
|
20
|
-
VellumIntegrationToolDefinition,
|
21
|
-
)
|
22
|
-
|
23
|
-
if TYPE_CHECKING:
|
24
|
-
from vellum.workflows.workflows.base import BaseWorkflow
|
25
|
-
|
26
13
|
|
27
14
|
JsonArray = List["Json"]
|
28
15
|
JsonObject = Dict[str, "Json"]
|
@@ -53,14 +40,3 @@ class ConditionType(Enum):
|
|
53
40
|
IF = "IF"
|
54
41
|
ELIF = "ELIF"
|
55
42
|
ELSE = "ELSE"
|
56
|
-
|
57
|
-
|
58
|
-
# Type alias for functions that can be called in tool calling nodes
|
59
|
-
ToolBase = Union[
|
60
|
-
Callable[..., Any],
|
61
|
-
DeploymentDefinition,
|
62
|
-
Type["BaseWorkflow"],
|
63
|
-
ComposioToolDefinition,
|
64
|
-
VellumIntegrationToolDefinition,
|
65
|
-
]
|
66
|
-
Tool = Union[ToolBase, MCPServer]
|
@@ -2,7 +2,7 @@ import importlib
|
|
2
2
|
import inspect
|
3
3
|
from types import FrameType
|
4
4
|
from uuid import UUID
|
5
|
-
from typing import Annotated, Any, Dict, List, Literal, Optional, Union
|
5
|
+
from typing import TYPE_CHECKING, Annotated, Any, Callable, Dict, List, Literal, Optional, Type, Union
|
6
6
|
|
7
7
|
from pydantic import BeforeValidator, SerializationInfo, model_serializer
|
8
8
|
|
@@ -13,6 +13,9 @@ from vellum.client.types.vellum_variable import VellumVariable
|
|
13
13
|
from vellum.workflows.constants import AuthorizationType, VellumIntegrationProviderType
|
14
14
|
from vellum.workflows.references.environment_variable import EnvironmentVariableReference
|
15
15
|
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
18
|
+
|
16
19
|
|
17
20
|
def serialize_type_encoder(obj: type) -> Dict[str, Any]:
|
18
21
|
return {
|
@@ -216,3 +219,14 @@ class MCPToolDefinition(UniversalBaseModel):
|
|
216
219
|
server: MCPServer
|
217
220
|
description: Optional[str] = None
|
218
221
|
parameters: Dict[str, Any] = {}
|
222
|
+
|
223
|
+
|
224
|
+
# Type alias for functions that can be called in tool calling nodes
|
225
|
+
ToolBase = Union[
|
226
|
+
Callable[..., Any],
|
227
|
+
DeploymentDefinition,
|
228
|
+
Type["BaseWorkflow"],
|
229
|
+
ComposioToolDefinition,
|
230
|
+
VellumIntegrationToolDefinition,
|
231
|
+
]
|
232
|
+
Tool = Union[ToolBase, MCPServer]
|
@@ -22,7 +22,7 @@ vellum_cli/tests/test_ping.py,sha256=b3aQLd-N59_8w2rRiWqwpB1rlHaKEYVbAj1Y3hi7A-g
|
|
22
22
|
vellum_cli/tests/test_pull.py,sha256=e2XHzcHIx9k-FyuNAl7wMSNsSSebPGyP6U05JGcddFs,49447
|
23
23
|
vellum_cli/tests/test_push.py,sha256=2MjkNKr_9Guv5Exjsm3L1BeVXmPkKUcCSiKnp90HgW4,41996
|
24
24
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
-
vellum_ee/assets/node-definitions.json,sha256=
|
25
|
+
vellum_ee/assets/node-definitions.json,sha256=UkHixt8WAnHmupooOrtpxYCaNG-5pRon1c8QUlc0Vhk,30754
|
26
26
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
vellum_ee/scripts/generate_node_definitions.py,sha256=FOYQsXIqU45I0OAcsyZUGODF9JK44yunf58rR6YaAdA,3303
|
28
28
|
vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -55,7 +55,7 @@ vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=Ue727X
|
|
55
55
|
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=5xv5OR4xRuI1R5yqJDZTNdCFYY-z8PkTdpWM4ziGjw0,3192
|
56
56
|
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=ZHqkwzCRsM0TkZmh3ePHreZAoQXeT-SFS7zbUrSjMsw,12037
|
57
57
|
vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=hC5hTSRf1f9ppiZ_wXzpcp-jl0fwNAUB9PY8s0AykHs,3131
|
58
|
-
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=
|
58
|
+
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=sWupmR7Ri2ZeCLx8713JbuawabR_kpWp_-HwT1tCrtU,3163
|
59
59
|
vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
60
|
vellum_ee/workflows/display/nodes/vellum/tests/test_api_node.py,sha256=DQAtsabvn6BE6xWwKNHzMOppzoy1-1dssNnrwbHUdRU,1490
|
61
61
|
vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py,sha256=6_1yVYftVnqXJn3hLUGHlcHvbgKbQgBfS6vYQ6R79oc,10891
|
@@ -79,7 +79,7 @@ vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=-LYQw5nWq
|
|
79
79
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
80
80
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
81
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=Y-ajeT65b5varmrZCw6L3hir4hJCFq-eO0jZfRcrs7g,1886
|
82
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=
|
82
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=wdVaFvxicj48Kj-6VUlu61KR0oSArLTjownRi2p0NWQ,14941
|
83
83
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=bPTwkLpB7trFLpAaDvXMfMP0c9H1u_c1cdnj7K-gtnw,24962
|
84
84
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=SwXRzjdoEZLvkzaRMvRV8_UqbBm0EB_UtAHD_zXKZBY,6303
|
85
85
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=jubGYgLJImOqILd5LjnYJ4B1UMIrToDrQbPZOvaQCX4,40035
|
@@ -126,7 +126,7 @@ vellum_ee/workflows/display/utils/tests/test_events.py,sha256=42IEBnMbaQrH8gigw5
|
|
126
126
|
vellum_ee/workflows/display/utils/vellum.py,sha256=Bt7kdLdXoBsHn5dVEY2uKcF542VL09jwu8J_30rl2vk,6413
|
127
127
|
vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
|
128
128
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
|
129
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
129
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=uP3pqSloGjmUYBvVfKP0skZXJnqZxm7TEVxSLdrzU5c,44682
|
130
130
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
|
131
131
|
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=lg-c_3P3ldtqWq2VFsk_2Mkn3pVdXWuT59QpH7QwXVs,39764
|
132
132
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -160,7 +160,7 @@ vellum/client/README.md,sha256=flqu57ubZNTfpq60CdLtJC9gp4WEkyjb_n_eZ4OYf9w,6497
|
|
160
160
|
vellum/client/__init__.py,sha256=rMnKRqL5-356SBc-rfm56MkO87PuAi2mtcfBszcJU1M,74316
|
161
161
|
vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
|
162
162
|
vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
|
163
|
-
vellum/client/core/client_wrapper.py,sha256=
|
163
|
+
vellum/client/core/client_wrapper.py,sha256=4Sfjvux7hceG2zs5IHHn9McKc2YE_WIb9ctx_HfCyzA,2840
|
164
164
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
165
165
|
vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
|
166
166
|
vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
|
@@ -1876,8 +1876,8 @@ vellum/workflows/integrations/composio_service.py,sha256=rSliaZtNiBcDSvDxz9k5i1K
|
|
1876
1876
|
vellum/workflows/integrations/mcp_service.py,sha256=9DYb8dg2_kgc1UOu830kxhaFlt9yTbhKPhK3L6kb1t4,9831
|
1877
1877
|
vellum/workflows/integrations/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1878
1878
|
vellum/workflows/integrations/tests/test_mcp_service.py,sha256=q_DYrDkIqI4sQBNgID4YdbM4e9tneLVWY8YmI4R26d8,8859
|
1879
|
-
vellum/workflows/integrations/tests/test_vellum_integration_service.py,sha256=
|
1880
|
-
vellum/workflows/integrations/vellum_integration_service.py,sha256=
|
1879
|
+
vellum/workflows/integrations/tests/test_vellum_integration_service.py,sha256=QGJmaW5EF7E2fkZotd4rc2HItQc-1z3cpgwaKUFWpgg,11956
|
1880
|
+
vellum/workflows/integrations/vellum_integration_service.py,sha256=qhFoLzHlMli1PC8oh5phvWuSpJ9IqL1g2eaGhypBTqs,5266
|
1881
1881
|
vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
|
1882
1882
|
vellum/workflows/nodes/__init__.py,sha256=zymtc3_iW2rFmMR-sayTLuN6ZsAw8VnJweWPsjQk2-Q,1197
|
1883
1883
|
vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
|
@@ -1931,11 +1931,11 @@ vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMD
|
|
1931
1931
|
vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
|
1932
1932
|
vellum/workflows/nodes/displayable/bases/utils.py,sha256=X1YSPmbBlxDrTAw6dy2-9HrIG8Vb_J-2k1lP3i-SOsk,6951
|
1933
1933
|
vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
|
1934
|
-
vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=
|
1934
|
+
vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=JkJ_p9KKmrkUqc8TPcxfPoZ1AiEnWWwrdzLj-0TS9ds,10985
|
1935
1935
|
vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1936
1936
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1937
1937
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
|
1938
|
-
vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=
|
1938
|
+
vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=eCWBaAPzneDiOQe1xFyyb9h9LeQQOAdjRY1RJS0z8sE,44500
|
1939
1939
|
vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=0F_4PVvB7vjsV0RS48Brv_4djebWOMR_zgzHxzA9iV4,3308
|
1940
1940
|
vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
|
1941
1941
|
vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=2g32hWosE3zwVaK2UPFnVEer1Nbn04s3friF2rGztmE,1195
|
@@ -1975,13 +1975,13 @@ vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py,sha2
|
|
1975
1975
|
vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=VepO5z1277c1y5N6LLIC31nnWD1aak2m5oPFplfJHHs,6935
|
1976
1976
|
vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=Bjv-wZyFgNaVZb9KEMMZd9lFoLzbPEPjEMpANizMZw4,2413
|
1977
1977
|
vellum/workflows/nodes/displayable/tool_calling_node/__init__.py,sha256=3n0-ysmFKsr40CVxPthc0rfJgqVJeZuUEsCmYudLVRg,117
|
1978
|
-
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=
|
1978
|
+
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=pwK9q1blgRv9Mz_LY-fv_hxHfOOkuKxJ6AgRlorUrXk,8420
|
1979
1979
|
vellum/workflows/nodes/displayable/tool_calling_node/state.py,sha256=CcBVb_YtwfSSka4ze678k6-qwmzMSfjfVP8_Y95feSo,302
|
1980
1980
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1981
1981
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=in1fbEz5x1tx3uKv9YXdvOncsHucNL8Ro6Go7lBuuOQ,8962
|
1982
1982
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=GZoeybB9uM7ai8sBLAtUMHrMVgh-WrJDWrKZci6feDs,11892
|
1983
1983
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=EmKFA-ELdTzlK0xMqWnuSZPoGNLYCwk6b0amTqirZo0,11305
|
1984
|
-
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=
|
1984
|
+
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=NDp2rEU-1QACxrSplocDKlPzILczzvPygkUOrysjU7Y,24109
|
1985
1985
|
vellum/workflows/nodes/displayable/web_search_node/__init__.py,sha256=8FOnEP-n-U68cvxTlJW9wphIAGHq5aqjzLM-DoSSXnU,61
|
1986
1986
|
vellum/workflows/nodes/displayable/web_search_node/node.py,sha256=NQYux2bOtuBF5E4tn-fXi5y3btURPRrNqMSM9MAZYI4,5091
|
1987
1987
|
vellum/workflows/nodes/displayable/web_search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -2017,14 +2017,14 @@ vellum/workflows/references/vellum_secret.py,sha256=Od4d19a5yletWMqNfJR5d_mZQUkV
|
|
2017
2017
|
vellum/workflows/references/workflow_input.py,sha256=W3rOK1EPd2gYHb04WJwmNm1CUSdvZ9LKrs8RMKxACBs,1751
|
2018
2018
|
vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
|
2019
2019
|
vellum/workflows/resolvers/base.py,sha256=wrQiSC02Bw4-dBwgFjJIHsjpe-4xz4rUJs_1RdErKA0,1164
|
2020
|
-
vellum/workflows/resolvers/resolver.py,sha256=
|
2021
|
-
vellum/workflows/resolvers/tests/test_resolver.py,sha256=
|
2020
|
+
vellum/workflows/resolvers/resolver.py,sha256=EIIiA2OlaVUPiKiSh6egQZxPA6ny1GDMdPq1AuN-mV8,2961
|
2021
|
+
vellum/workflows/resolvers/tests/test_resolver.py,sha256=PnUGzsulo1It_LjjhHsRNiILvvl5G_IaK8ZX56zKC28,6204
|
2022
2022
|
vellum/workflows/resolvers/types.py,sha256=Hndhlk69g6EKLh_LYg5ILepW5U_h_BYNllfzhS9k8p4,237
|
2023
2023
|
vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
|
2024
2024
|
vellum/workflows/runner/runner.py,sha256=rwMZlQBkzK-EfewC6OysNCeuDeTCfpNuzaUlgYoFJMw,43329
|
2025
2025
|
vellum/workflows/sandbox.py,sha256=mezSZmilR_fwR8164n8CEfzlMeQ55IqfapHp4ftImvQ,3212
|
2026
2026
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
2027
|
-
vellum/workflows/state/base.py,sha256=
|
2027
|
+
vellum/workflows/state/base.py,sha256=A8s0PC8UvFjPpkHDY6u-yIeb2KHjoAmu-GW-GYrDl0E,24654
|
2028
2028
|
vellum/workflows/state/context.py,sha256=khM30U1iDNts5Xp8LXa_WfpkITNITexrDUUFJ5wZ2W4,8445
|
2029
2029
|
vellum/workflows/state/delta.py,sha256=7h8wR10lRCm15SykaPj-gSEvvsMjCwYLPsOx3nsvBQg,440
|
2030
2030
|
vellum/workflows/state/encoder.py,sha256=EynuS9aCt9Neb-H6HRCinEVZX5olCzME03W1TSXfpxs,1961
|
@@ -2037,8 +2037,8 @@ vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVi
|
|
2037
2037
|
vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
|
2038
2038
|
vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
|
2039
2039
|
vellum/workflows/types/code_execution_node_wrappers.py,sha256=fewX9bqF_4TZuK-gZYIn12s31-k03vHMGRpvFAPm11Y,3206
|
2040
|
-
vellum/workflows/types/core.py,sha256=
|
2041
|
-
vellum/workflows/types/definition.py,sha256=
|
2040
|
+
vellum/workflows/types/core.py,sha256=X8xNBLUhnea97t3qeeZJ2XyUwW_a38YiznXr73j_ppE,950
|
2041
|
+
vellum/workflows/types/definition.py,sha256=Qof2MAjSNB0AN2XkSKmk-owuY59YcxDVHYpno6-StPA,8058
|
2042
2042
|
vellum/workflows/types/generics.py,sha256=8jptbEx1fnJV0Lhj0MpCJOT6yNiEWeTOYOwrEAb5CRU,1576
|
2043
2043
|
vellum/workflows/types/stack.py,sha256=h7NE0vXR7l9DevFBIzIAk1Zh59K-kECQtDTKOUunwMY,1314
|
2044
2044
|
vellum/workflows/types/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -2065,8 +2065,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
2065
2065
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2066
2066
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=Boa-_m9ii2Qsa1RvVM-VYniF7zCpzGgEGy-OnPZkrHg,23941
|
2067
2067
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
2068
|
-
vellum_ai-1.7.
|
2069
|
-
vellum_ai-1.7.
|
2070
|
-
vellum_ai-1.7.
|
2071
|
-
vellum_ai-1.7.
|
2072
|
-
vellum_ai-1.7.
|
2068
|
+
vellum_ai-1.7.2.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
2069
|
+
vellum_ai-1.7.2.dist-info/METADATA,sha256=KyFKrTqcT-6Pvl5zTeo1EsEP0-uY4WREJPAr8Vdkh4A,5547
|
2070
|
+
vellum_ai-1.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
2071
|
+
vellum_ai-1.7.2.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
|
2072
|
+
vellum_ai-1.7.2.dist-info/RECORD,,
|
@@ -699,6 +699,51 @@
|
|
699
699
|
}
|
700
700
|
]
|
701
701
|
},
|
702
|
+
{
|
703
|
+
"id": "ee9b5234-247b-49d1-bed4-490312f18838",
|
704
|
+
"display_data": {
|
705
|
+
"position": {
|
706
|
+
"x": 0.0,
|
707
|
+
"y": 0.0
|
708
|
+
},
|
709
|
+
"comment": {
|
710
|
+
"value": "Used to render a Jinja template.\n\n Useful for lightweight data transformations and complex string templating.\n ",
|
711
|
+
"expanded": true
|
712
|
+
}
|
713
|
+
},
|
714
|
+
"base": {
|
715
|
+
"name": "BaseNode",
|
716
|
+
"module": [
|
717
|
+
"vellum",
|
718
|
+
"workflows",
|
719
|
+
"nodes",
|
720
|
+
"bases",
|
721
|
+
"base"
|
722
|
+
]
|
723
|
+
},
|
724
|
+
"definition": {
|
725
|
+
"name": "TemplatingNode",
|
726
|
+
"module": [
|
727
|
+
"vellum",
|
728
|
+
"workflows",
|
729
|
+
"nodes",
|
730
|
+
"core",
|
731
|
+
"templating_node",
|
732
|
+
"node"
|
733
|
+
]
|
734
|
+
},
|
735
|
+
"trigger": {
|
736
|
+
"id": "005e1cd2-5452-4e1f-be6a-ac9fe3c02b9b",
|
737
|
+
"merge_behavior": "AWAIT_ATTRIBUTES"
|
738
|
+
},
|
739
|
+
"ports": [
|
740
|
+
{
|
741
|
+
"id": "8f4460f0-717b-4972-a6f4-ac164e5e204e",
|
742
|
+
"name": "default",
|
743
|
+
"type": "DEFAULT"
|
744
|
+
}
|
745
|
+
]
|
746
|
+
},
|
702
747
|
{
|
703
748
|
"id": "035842e0-34ed-43af-97de-a706e40912ae",
|
704
749
|
"label": "Tool Calling Node",
|
@@ -1010,10 +1055,5 @@
|
|
1010
1055
|
]
|
1011
1056
|
}
|
1012
1057
|
],
|
1013
|
-
"errors": [
|
1014
|
-
{
|
1015
|
-
"node": "TemplatingNode",
|
1016
|
-
"error": "KeyError: TemplatingNode.Outputs.result"
|
1017
|
-
}
|
1018
|
-
]
|
1058
|
+
"errors": []
|
1019
1059
|
}
|
@@ -50,7 +50,7 @@ class BaseTemplatingNodeDisplay(BaseNodeDisplay[_TemplatingNodeType], Generic[_T
|
|
50
50
|
# Misc type ignore is due to `node.Outputs` being generic
|
51
51
|
# https://app.shortcut.com/vellum/story/4784
|
52
52
|
output_descriptor = node.Outputs.result # type: ignore [misc]
|
53
|
-
output_display =
|
53
|
+
output_display = self.get_node_output_display(output_descriptor)
|
54
54
|
inferred_output_type = primitive_type_to_vellum_variable_type(output_descriptor)
|
55
55
|
|
56
56
|
return {
|
@@ -353,3 +353,33 @@ def test_serialize_node__adornment_order_matches_decorator_order():
|
|
353
353
|
assert len(adornments) == 2
|
354
354
|
assert adornments[0]["label"] == "Try Node"
|
355
355
|
assert adornments[1]["label"] == "Retry Node"
|
356
|
+
|
357
|
+
|
358
|
+
def test_serialize_workflow__retry_node_edges():
|
359
|
+
"""
|
360
|
+
Tests that both retry-adorned nodes are correctly serialized in the nodes array.
|
361
|
+
"""
|
362
|
+
|
363
|
+
@RetryNode.wrap(max_attempts=3, delay=60)
|
364
|
+
class FirstNode(BaseNode):
|
365
|
+
class Outputs(BaseOutputs):
|
366
|
+
value: str
|
367
|
+
|
368
|
+
@RetryNode.wrap(max_attempts=5, delay=120)
|
369
|
+
class SecondNode(BaseNode):
|
370
|
+
class Outputs(BaseOutputs):
|
371
|
+
result: str
|
372
|
+
|
373
|
+
class MyWorkflow(BaseWorkflow):
|
374
|
+
graph = FirstNode >> SecondNode
|
375
|
+
|
376
|
+
workflow_display = get_workflow_display(workflow_class=MyWorkflow)
|
377
|
+
exec_config = cast(Dict[str, Any], workflow_display.serialize())
|
378
|
+
|
379
|
+
assert isinstance(exec_config["workflow_raw_data"], dict)
|
380
|
+
assert isinstance(exec_config["workflow_raw_data"]["nodes"], list)
|
381
|
+
|
382
|
+
nodes = cast(List[Dict[str, Any]], exec_config["workflow_raw_data"]["nodes"])
|
383
|
+
|
384
|
+
generic_nodes = [node for node in nodes if node["type"] == "GENERIC"]
|
385
|
+
assert len(generic_nodes) == 2
|
@@ -208,7 +208,15 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
208
208
|
self.display_context.add_invalid_node(node)
|
209
209
|
continue
|
210
210
|
|
211
|
-
|
211
|
+
# Use wrapped node's ID as dict key for adornment wrappers to prevent overwrites
|
212
|
+
wrapped_node = get_wrapped_node(node)
|
213
|
+
if wrapped_node:
|
214
|
+
wrapped_node_display = self.display_context.node_displays[wrapped_node]
|
215
|
+
dict_key = wrapped_node_display.node_id
|
216
|
+
else:
|
217
|
+
dict_key = node_display.node_id
|
218
|
+
|
219
|
+
serialized_nodes[dict_key] = serialized_node
|
212
220
|
|
213
221
|
synthetic_output_edges: JsonArray = []
|
214
222
|
output_variables: JsonArray = []
|
File without changes
|
File without changes
|
File without changes
|