vellum-ai 1.5.1__py3-none-any.whl → 1.5.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.
@@ -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.5.1",
30
+ "User-Agent": "vellum-ai/1.5.2",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.5.1",
33
+ "X-Fern-SDK-Version": "1.5.2",
34
34
  **(self.get_custom_headers() or {}),
35
35
  }
36
36
  if self._api_version is not None:
@@ -1,225 +1,206 @@
1
1
  import pytest
2
2
  from unittest import mock
3
3
 
4
+ from vellum.workflows.constants import VellumIntegrationProviderType
4
5
  from vellum.workflows.exceptions import NodeException
5
6
  from vellum.workflows.integrations.vellum_integration_service import VellumIntegrationService
7
+ from vellum.workflows.types.definition import VellumIntegrationToolDetails
6
8
 
7
9
 
8
- def test_vellum_integration_service_get_tool_definition_success():
10
+ def test_vellum_integration_service_get_tool_definition_success(vellum_client):
9
11
  """Test that tool definitions are successfully retrieved from Vellum API"""
10
- with mock.patch(
11
- "vellum.workflows.integrations.vellum_integration_service.create_vellum_client"
12
- ) as mock_create_client:
13
- # GIVEN a mock Vellum client configured to return a tool definition
14
- mock_client = mock.MagicMock()
15
- mock_create_client.return_value = mock_client
16
-
17
- mock_response = mock.MagicMock()
18
- mock_response.name = "GITHUB_CREATE_AN_ISSUE"
19
- mock_response.description = "Create a new issue in a GitHub repository"
20
- mock_response.parameters = {
21
- "type": "object",
22
- "properties": {
23
- "repo": {"type": "string", "description": "Repository name"},
24
- "title": {"type": "string", "description": "Issue title"},
25
- "body": {"type": "string", "description": "Issue body"},
26
- },
27
- "required": ["repo", "title"],
28
- }
29
- mock_response.provider = "COMPOSIO"
30
-
31
- mock_client.integrations.retrieve_integration_tool_definition.return_value = mock_response
32
-
33
- # WHEN we request a tool definition
34
- service = VellumIntegrationService()
35
- result = service.get_tool_definition(
36
- integration="GITHUB",
37
- provider="COMPOSIO",
38
- tool_name="GITHUB_CREATE_AN_ISSUE",
39
- )
12
+ mock_client = vellum_client
13
+ mock_client.integrations = mock.MagicMock()
14
+
15
+ mock_response = mock.MagicMock()
16
+ mock_response.name = "GITHUB_CREATE_AN_ISSUE"
17
+ mock_response.description = "Create a new issue in a GitHub repository"
18
+ mock_response.parameters = {
19
+ "type": "object",
20
+ "properties": {
21
+ "repo": {"type": "string", "description": "Repository name"},
22
+ "title": {"type": "string", "description": "Issue title"},
23
+ "body": {"type": "string", "description": "Issue body"},
24
+ },
25
+ "required": ["repo", "title"],
26
+ }
27
+ mock_response.provider = "COMPOSIO"
28
+
29
+ mock_client.integrations.retrieve_integration_tool_definition.return_value = mock_response
30
+
31
+ # WHEN we request a tool definition
32
+ service = VellumIntegrationService(client=mock_client)
33
+ result = service.get_tool_definition(
34
+ integration="GITHUB",
35
+ provider="COMPOSIO",
36
+ tool_name="GITHUB_CREATE_AN_ISSUE",
37
+ )
38
+
39
+ # THEN the tool definition should be returned with all expected fields
40
+ assert isinstance(result, VellumIntegrationToolDetails)
41
+ assert result.name == "GITHUB_CREATE_AN_ISSUE"
42
+ assert result.description == "Create a new issue in a GitHub repository"
43
+ assert result.provider == VellumIntegrationProviderType.COMPOSIO
44
+ # Parameters should now be included in the tool details
45
+ assert result.parameters is not None
46
+ assert result.parameters["type"] == "object"
47
+ assert "properties" in result.parameters
48
+ assert "repo" in result.parameters["properties"]
49
+ assert "title" in result.parameters["properties"]
50
+
51
+ # AND the API should have been called with the correct parameters
52
+ mock_client.integrations.retrieve_integration_tool_definition.assert_called_once_with(
53
+ integration="GITHUB",
54
+ provider="COMPOSIO",
55
+ tool_name="GITHUB_CREATE_AN_ISSUE",
56
+ )
57
+
58
+
59
+ def test_vellum_integration_service_get_tool_definition_api_error(vellum_client):
60
+ """Test that API errors are properly handled when retrieving tool definitions"""
61
+ mock_client = vellum_client
62
+ mock_client.integrations = mock.MagicMock()
63
+ mock_client.integrations.retrieve_integration_tool_definition.side_effect = Exception("Tool not found")
40
64
 
41
- # THEN the tool definition should be returned with all expected fields
42
- assert result["name"] == "GITHUB_CREATE_AN_ISSUE"
43
- assert result["description"] == "Create a new issue in a GitHub repository"
44
- assert result["provider"] == "COMPOSIO"
45
- assert "properties" in result["parameters"]
46
- assert "repo" in result["parameters"]["properties"]
65
+ # WHEN we attempt to get a tool definition for an invalid tool
66
+ service = VellumIntegrationService(client=mock_client)
47
67
 
48
- # AND the API should have been called with the correct parameters
49
- mock_client.integrations.retrieve_integration_tool_definition.assert_called_once_with(
68
+ # THEN it should raise a NodeException with appropriate error message
69
+ with pytest.raises(NodeException) as exc_info:
70
+ service.get_tool_definition(
50
71
  integration="GITHUB",
51
72
  provider="COMPOSIO",
52
- tool_name="GITHUB_CREATE_AN_ISSUE",
73
+ tool_name="INVALID_TOOL",
53
74
  )
54
75
 
55
-
56
- def test_vellum_integration_service_get_tool_definition_api_error():
57
- """Test that API errors are properly handled when retrieving tool definitions"""
58
- with mock.patch(
59
- "vellum.workflows.integrations.vellum_integration_service.create_vellum_client"
60
- ) as mock_create_client:
61
- # GIVEN a mock client that raises an exception when retrieving tool definitions
62
- mock_client = mock.MagicMock()
63
- mock_create_client.return_value = mock_client
64
-
65
- mock_client.integrations.retrieve_integration_tool_definition.side_effect = Exception("Tool not found")
66
-
67
- # WHEN we attempt to get a tool definition for an invalid tool
68
- service = VellumIntegrationService()
69
-
70
- # THEN it should raise a NodeException with appropriate error message
71
- with pytest.raises(NodeException) as exc_info:
72
- service.get_tool_definition(
73
- integration="GITHUB",
74
- provider="COMPOSIO",
75
- tool_name="INVALID_TOOL",
76
- )
77
-
78
- assert "Failed to retrieve tool definition" in str(exc_info.value)
79
- assert "Tool not found" in str(exc_info.value)
76
+ assert "Failed to retrieve tool definition" in str(exc_info.value)
77
+ assert "Tool not found" in str(exc_info.value)
80
78
 
81
79
 
82
- def test_vellum_integration_service_execute_tool_success():
80
+ def test_vellum_integration_service_execute_tool_success(vellum_client):
83
81
  """Test that tools are successfully executed via Vellum API"""
84
- with mock.patch(
85
- "vellum.workflows.integrations.vellum_integration_service.create_vellum_client"
86
- ) as mock_create_client:
87
- # GIVEN a mock client configured to return successful execution results
88
- mock_client = mock.MagicMock()
89
- mock_create_client.return_value = mock_client
90
-
91
- mock_response = mock.MagicMock()
92
- mock_response.data = {
93
- "success": True,
94
- "issue_id": 123,
95
- "issue_url": "https://github.com/user/repo/issues/123",
96
- }
97
-
98
- mock_client.integrations.execute_integration_tool.return_value = mock_response
99
-
100
- # WHEN we execute a tool with valid arguments
101
- service = VellumIntegrationService()
102
- result = service.execute_tool(
103
- integration="GITHUB",
104
- provider="COMPOSIO",
105
- tool_name="GITHUB_CREATE_AN_ISSUE",
106
- arguments={
107
- "repo": "user/repo",
108
- "title": "Test Issue",
109
- "body": "Test body",
110
- },
111
- )
82
+ mock_client = vellum_client
83
+ mock_client.integrations = mock.MagicMock()
84
+
85
+ mock_response = mock.MagicMock()
86
+ mock_response.data = {
87
+ "success": True,
88
+ "issue_id": 123,
89
+ "issue_url": "https://github.com/user/repo/issues/123",
90
+ }
91
+
92
+ mock_client.integrations.execute_integration_tool.return_value = mock_response
93
+
94
+ # WHEN we execute a tool with valid arguments
95
+ service = VellumIntegrationService(client=mock_client)
96
+ result = service.execute_tool(
97
+ integration="GITHUB",
98
+ provider="COMPOSIO",
99
+ tool_name="GITHUB_CREATE_AN_ISSUE",
100
+ arguments={
101
+ "repo": "user/repo",
102
+ "title": "Test Issue",
103
+ "body": "Test body",
104
+ },
105
+ )
106
+
107
+ # THEN the execution result should contain expected data
108
+ assert result["success"] is True
109
+ assert result["issue_id"] == 123
110
+ assert result["issue_url"] == "https://github.com/user/repo/issues/123"
111
+
112
+ # AND the API should have been called with correct parameters
113
+ mock_client.integrations.execute_integration_tool.assert_called_once_with(
114
+ integration="GITHUB",
115
+ provider="COMPOSIO",
116
+ tool_name="GITHUB_CREATE_AN_ISSUE",
117
+ arguments={
118
+ "repo": "user/repo",
119
+ "title": "Test Issue",
120
+ "body": "Test body",
121
+ },
122
+ )
123
+
124
+
125
+ def test_vellum_integration_service_execute_tool_api_error(vellum_client):
126
+ """Test that execution errors are properly handled"""
127
+ mock_client = vellum_client
128
+ mock_client.integrations = mock.MagicMock()
129
+ mock_client.integrations.execute_integration_tool.side_effect = Exception("Authentication failed")
112
130
 
113
- # THEN the execution result should contain expected data
114
- assert result["success"] is True
115
- assert result["issue_id"] == 123
116
- assert result["issue_url"] == "https://github.com/user/repo/issues/123"
131
+ # WHEN we attempt to execute a tool that encounters an error
132
+ service = VellumIntegrationService(client=mock_client)
117
133
 
118
- # AND the API should have been called with correct parameters
119
- mock_client.integrations.execute_integration_tool.assert_called_once_with(
134
+ # THEN it should raise a NodeException with appropriate error message
135
+ with pytest.raises(NodeException) as exc_info:
136
+ service.execute_tool(
120
137
  integration="GITHUB",
121
138
  provider="COMPOSIO",
122
139
  tool_name="GITHUB_CREATE_AN_ISSUE",
123
- arguments={
124
- "repo": "user/repo",
125
- "title": "Test Issue",
126
- "body": "Test body",
127
- },
140
+ arguments={"repo": "user/repo"},
128
141
  )
129
142
 
143
+ assert "Failed to execute tool" in str(exc_info.value)
144
+ assert "Authentication failed" in str(exc_info.value)
130
145
 
131
- def test_vellum_integration_service_execute_tool_api_error():
132
- """Test that execution errors are properly handled"""
133
- with mock.patch(
134
- "vellum.workflows.integrations.vellum_integration_service.create_vellum_client"
135
- ) as mock_create_client:
136
- # GIVEN a mock client that raises an exception during tool execution
137
- mock_client = mock.MagicMock()
138
- mock_create_client.return_value = mock_client
139
-
140
- mock_client.integrations.execute_integration_tool.side_effect = Exception("Authentication failed")
141
-
142
- # WHEN we attempt to execute a tool that encounters an error
143
- service = VellumIntegrationService()
144
146
 
145
- # THEN it should raise a NodeException with appropriate error message
146
- with pytest.raises(NodeException) as exc_info:
147
- service.execute_tool(
148
- integration="GITHUB",
149
- provider="COMPOSIO",
150
- tool_name="GITHUB_CREATE_AN_ISSUE",
151
- arguments={"repo": "user/repo"},
152
- )
153
-
154
- assert "Failed to execute tool" in str(exc_info.value)
155
- assert "Authentication failed" in str(exc_info.value)
156
-
157
-
158
- def test_vellum_integration_service_execute_tool_empty_response():
147
+ def test_vellum_integration_service_execute_tool_empty_response(vellum_client):
159
148
  """Test that empty response data is handled gracefully"""
160
- with mock.patch(
161
- "vellum.workflows.integrations.vellum_integration_service.create_vellum_client"
162
- ) as mock_create_client:
163
- # GIVEN a mock client that returns an empty response
164
- mock_client = mock.MagicMock()
165
- mock_create_client.return_value = mock_client
166
-
167
- mock_response = mock.MagicMock()
168
- mock_response.data = {}
169
-
170
- mock_client.integrations.execute_integration_tool.return_value = mock_response
171
-
172
- # WHEN we execute a tool that returns empty data
173
- service = VellumIntegrationService()
174
- result = service.execute_tool(
175
- integration="SLACK",
176
- provider="COMPOSIO",
177
- tool_name="SLACK_SEND_MESSAGE",
178
- arguments={
179
- "channel": "#general",
180
- "message": "Hello, world!",
181
- },
182
- )
149
+ mock_client = vellum_client
150
+ mock_client.integrations = mock.MagicMock()
183
151
 
184
- # THEN an empty dictionary should be returned without errors
185
- assert result == {}
152
+ mock_response = mock.MagicMock()
153
+ mock_response.data = {}
186
154
 
155
+ mock_client.integrations.execute_integration_tool.return_value = mock_response
187
156
 
188
- def test_vellum_integration_service_multiple_tool_executions():
189
- """Test that the service handles multiple sequential tool executions"""
190
- with mock.patch(
191
- "vellum.workflows.integrations.vellum_integration_service.create_vellum_client"
192
- ) as mock_create_client:
193
- # GIVEN a mock client configured to return different responses for each call
194
- mock_client = mock.MagicMock()
195
- mock_create_client.return_value = mock_client
196
-
197
- responses = [
198
- mock.MagicMock(data={"result": "first"}),
199
- mock.MagicMock(data={"result": "second"}),
200
- ]
201
- mock_client.integrations.execute_integration_tool.side_effect = responses
202
-
203
- # WHEN we execute multiple tools in sequence
204
- service = VellumIntegrationService()
205
-
206
- result1 = service.execute_tool(
207
- integration="GITHUB",
208
- provider="COMPOSIO",
209
- tool_name="TOOL_1",
210
- arguments={"arg": "val1"},
211
- )
157
+ # WHEN we execute a tool that returns empty data
158
+ service = VellumIntegrationService(client=mock_client)
159
+ result = service.execute_tool(
160
+ integration="SLACK",
161
+ provider="COMPOSIO",
162
+ tool_name="SLACK_SEND_MESSAGE",
163
+ arguments={
164
+ "channel": "#general",
165
+ "message": "Hello, world!",
166
+ },
167
+ )
212
168
 
213
- result2 = service.execute_tool(
214
- integration="SLACK",
215
- provider="COMPOSIO",
216
- tool_name="TOOL_2",
217
- arguments={"arg": "val2"},
218
- )
169
+ # THEN an empty dictionary should be returned without errors
170
+ assert result == {}
219
171
 
220
- # THEN each tool execution should return its respective result
221
- assert result1["result"] == "first"
222
- assert result2["result"] == "second"
223
172
 
224
- # AND the API should have been called twice
225
- assert mock_client.integrations.execute_integration_tool.call_count == 2
173
+ def test_vellum_integration_service_multiple_tool_executions(vellum_client):
174
+ """Test that the service handles multiple sequential tool executions"""
175
+ mock_client = vellum_client
176
+ mock_client.integrations = mock.MagicMock()
177
+
178
+ responses = [
179
+ mock.MagicMock(data={"result": "first"}),
180
+ mock.MagicMock(data={"result": "second"}),
181
+ ]
182
+ mock_client.integrations.execute_integration_tool.side_effect = responses
183
+
184
+ # WHEN we execute multiple tools in sequence
185
+ service = VellumIntegrationService(client=mock_client)
186
+
187
+ result1 = service.execute_tool(
188
+ integration="GITHUB",
189
+ provider="COMPOSIO",
190
+ tool_name="TOOL_1",
191
+ arguments={"arg": "val1"},
192
+ )
193
+
194
+ result2 = service.execute_tool(
195
+ integration="SLACK",
196
+ provider="COMPOSIO",
197
+ tool_name="TOOL_2",
198
+ arguments={"arg": "val2"},
199
+ )
200
+
201
+ # THEN each tool execution should return its respective result
202
+ assert result1["result"] == "first"
203
+ assert result2["result"] == "second"
204
+
205
+ # AND the API should have been called twice
206
+ assert mock_client.integrations.execute_integration_tool.call_count == 2
@@ -1,7 +1,9 @@
1
- from typing import Any, Dict
1
+ from typing import Any, Dict, Optional
2
2
 
3
+ from vellum.workflows.constants import VellumIntegrationProviderType
3
4
  from vellum.workflows.errors.types import WorkflowErrorCode
4
5
  from vellum.workflows.exceptions import NodeException
6
+ from vellum.workflows.types.definition import VellumIntegrationToolDetails
5
7
  from vellum.workflows.vellum_client import create_vellum_client
6
8
 
7
9
 
@@ -13,16 +15,16 @@ class VellumIntegrationService:
13
15
  own integration infrastructure.
14
16
  """
15
17
 
16
- def __init__(self) -> None:
18
+ def __init__(self, client: Optional[Any] = None) -> None:
17
19
  """Initialize the VellumIntegrationService with a Vellum client."""
18
- self._client = create_vellum_client()
20
+ self._client = client or create_vellum_client()
19
21
 
20
22
  def get_tool_definition(
21
23
  self,
22
24
  integration: str,
23
25
  provider: str,
24
26
  tool_name: str,
25
- ) -> Dict[str, Any]:
27
+ ) -> VellumIntegrationToolDetails:
26
28
  """Retrieve a tool definition from Vellum integrations.
27
29
 
28
30
  Args:
@@ -31,7 +33,7 @@ class VellumIntegrationService:
31
33
  tool_name: The tool's unique name as specified by the provider
32
34
 
33
35
  Returns:
34
- Dict containing the tool definition with name, description, and parameters
36
+ VellumIntegrationToolDetails containing the tool definition with parameters
35
37
 
36
38
  Raises:
37
39
  NodeException: If the tool definition cannot be retrieved
@@ -43,13 +45,13 @@ class VellumIntegrationService:
43
45
  tool_name=tool_name,
44
46
  )
45
47
 
46
- # Convert the response to a dict format matching what's expected
47
- return {
48
- "name": response.name,
49
- "description": response.description,
50
- "parameters": response.parameters,
51
- "provider": response.provider,
52
- }
48
+ return VellumIntegrationToolDetails(
49
+ provider=VellumIntegrationProviderType(response.provider),
50
+ integration=integration,
51
+ name=response.name,
52
+ description=response.description,
53
+ parameters=response.parameters,
54
+ )
53
55
  except Exception as e:
54
56
  error_message = f"Failed to retrieve tool definition for {tool_name}: {str(e)}"
55
57
  raise NodeException(
@@ -177,6 +177,17 @@ class VellumIntegrationToolDefinition(UniversalBaseModel):
177
177
  description: str
178
178
 
179
179
 
180
+ class VellumIntegrationToolDetails(VellumIntegrationToolDefinition):
181
+ """Extended version of VellumIntegrationToolDefinition with runtime parameters.
182
+
183
+ This class includes the parameters field which is populated during compilation
184
+ from the Vellum integrations API response. It inherits all fields from the base
185
+ VellumIntegrationToolDefinition class.
186
+ """
187
+
188
+ parameters: Optional[Dict[str, Any]] = None
189
+
190
+
180
191
  class MCPServer(UniversalBaseModel):
181
192
  type: Literal["MCP_SERVER"] = "MCP_SERVER"
182
193
  name: str
@@ -335,13 +335,15 @@ def compile_vellum_integration_tool_definition(tool_def: VellumIntegrationToolDe
335
335
  try:
336
336
  service = VellumIntegrationService()
337
337
  tool_details = service.get_tool_definition(
338
- integration=tool_def.integration, provider=tool_def.provider.value, tool_name=tool_def.name
338
+ integration=tool_def.integration,
339
+ provider=tool_def.provider.value,
340
+ tool_name=tool_def.name,
339
341
  )
340
342
 
341
343
  return FunctionDefinition(
342
344
  name=tool_def.name,
343
- description=tool_details.get("description", tool_def.description),
344
- parameters=tool_details.get("parameters", {}),
345
+ description=tool_details.description,
346
+ parameters=tool_details.parameters or {},
345
347
  )
346
348
  except Exception:
347
349
  # Fallback for service failures
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.5.1
3
+ Version: 1.5.2
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -22,9 +22,9 @@ 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=Sn4fy0oUUCd_mj7vRNvQI5XR88A7-HKbe9pWaVnbz28,24293
25
+ vellum_ee/assets/node-definitions.json,sha256=UDx8gBZ4Vj853aY7CSHR8JipVjZSQiKdkOKwKqwwVKw,25499
26
26
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- vellum_ee/scripts/generate_node_definitions.py,sha256=1pVf2EA3LeX4kLKC2hSanyk2ZZDGVZ_lb83TE416XP8,3274
27
+ vellum_ee/scripts/generate_node_definitions.py,sha256=FOYQsXIqU45I0OAcsyZUGODF9JK44yunf58rR6YaAdA,3303
28
28
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  vellum_ee/workflows/display/base.py,sha256=R3f2T8FlZrXn2FawAmpVuLB3fKFWw11mCUulWAyIKA0,1912
@@ -48,7 +48,7 @@ vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=z4oeTgKnMGV
48
48
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=9_AslWjzj4RHH2sq3SIaq9FU0NCg7ex5TIWrNMybqXg,2173
49
49
  vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=muyLv3KMIsUnnXhiPbPhw5B0TO1Z8LUwytpVQKlz4tM,11906
50
50
  vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=m20_ZZ3Au0ZCpI3TNC9xh54ld1X13CNq-T51VOtP23k,6434
51
- vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=kwLqptup7bzYUDkGDbpcJPMMusMezsYrG5rSUYl5TlQ,3750
51
+ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=AW5JuHXtmtcJKeRiZKd7iB1re_D7G7jl_OlaZs8nUl0,4219
52
52
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=xMHaPfTSZWYprQenlHm2g47u0a5O9Me_dhAjfqo8nKQ,3116
53
53
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=6PcAFA_EJn7vEMdqgoRjYTLHwnXCrJv80B10zuUx4jE,1026
54
54
  vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=uo4YZRV48iX4peGAA1xkGniSPrepywvW_gS7codt-VQ,3378
@@ -158,7 +158,7 @@ vellum/client/README.md,sha256=flqu57ubZNTfpq60CdLtJC9gp4WEkyjb_n_eZ4OYf9w,6497
158
158
  vellum/client/__init__.py,sha256=-dZaD_0KtlkpQ-pULNNWcarC5xXlGMcGc2nBKLIyRlA,73661
159
159
  vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
160
160
  vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
161
- vellum/client/core/client_wrapper.py,sha256=HGgXu4osF39E2IYXXZKqZ_UBngm-WseBYRAmezfoveA,2840
161
+ vellum/client/core/client_wrapper.py,sha256=dgP8KuamHuhB9q-zUV2SOGwlGkksYCbOSO1zmJRGB7A,2840
162
162
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
163
163
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
164
164
  vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
@@ -1824,8 +1824,8 @@ vellum/workflows/integrations/composio_service.py,sha256=rSliaZtNiBcDSvDxz9k5i1K
1824
1824
  vellum/workflows/integrations/mcp_service.py,sha256=9DYb8dg2_kgc1UOu830kxhaFlt9yTbhKPhK3L6kb1t4,9831
1825
1825
  vellum/workflows/integrations/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1826
1826
  vellum/workflows/integrations/tests/test_mcp_service.py,sha256=q_DYrDkIqI4sQBNgID4YdbM4e9tneLVWY8YmI4R26d8,8859
1827
- vellum/workflows/integrations/tests/test_vellum_integration_service.py,sha256=5Cw9fqw2DfIlD1h9xjso2WdocyhELVG8RMpMttUiqQ4,8903
1828
- vellum/workflows/integrations/vellum_integration_service.py,sha256=0pD__eFjhKpmkF110tW2L6btr9CCUOKHMo5mBU7Ykrs,3405
1827
+ vellum/workflows/integrations/tests/test_vellum_integration_service.py,sha256=qGDV1Tx3cWoaazjfnkv8MutLYscwvSZazRqtnz3EPhY,7607
1828
+ vellum/workflows/integrations/vellum_integration_service.py,sha256=AzF2QkEcLspTV5rySA2GToirNpMmTiY07OZx-6Vq3IA,3625
1829
1829
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1830
1830
  vellum/workflows/nodes/__init__.py,sha256=zymtc3_iW2rFmMR-sayTLuN6ZsAw8VnJweWPsjQk2-Q,1197
1831
1831
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
@@ -1985,7 +1985,7 @@ vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83y
1985
1985
  vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
1986
1986
  vellum/workflows/types/code_execution_node_wrappers.py,sha256=fewX9bqF_4TZuK-gZYIn12s31-k03vHMGRpvFAPm11Y,3206
1987
1987
  vellum/workflows/types/core.py,sha256=yKm3sE02ult969q80DTmawiwYqodVjcAW-zlaUIgIv4,1495
1988
- vellum/workflows/types/definition.py,sha256=rVoiXhj7xcQS793qjt2gdv64ywfQrRvujURjIWeC6gA,7240
1988
+ vellum/workflows/types/definition.py,sha256=_wzpa0CtS60IIEPfhrLXmj2y9datz5XqJZhLYpml-NI,7663
1989
1989
  vellum/workflows/types/generics.py,sha256=8jptbEx1fnJV0Lhj0MpCJOT6yNiEWeTOYOwrEAb5CRU,1576
1990
1990
  vellum/workflows/types/stack.py,sha256=h7NE0vXR7l9DevFBIzIAk1Zh59K-kECQtDTKOUunwMY,1314
1991
1991
  vellum/workflows/types/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1993,7 +1993,7 @@ vellum/workflows/types/tests/test_definition.py,sha256=rvDYjdJ1rvAv0qHBN7i7s-_WA
1993
1993
  vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
1994
1994
  vellum/workflows/types/utils.py,sha256=mTctHITBybpt4855x32oCKALBEcMNLn-9cCmfEKgJHQ,6498
1995
1995
  vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1996
- vellum/workflows/utils/functions.py,sha256=9KqkCOpuTEncjAxGHfcMGIsrD8-yFQp8NgzvU3w5Rd8,13066
1996
+ vellum/workflows/utils/functions.py,sha256=_esuDTYKEORjBoC0YGT3ANO--OswaKu6s-_42_kzlwU,13057
1997
1997
  vellum/workflows/utils/hmac.py,sha256=JJCczc6pyV6DuE1Oa0QVfYPUN_of3zEYmGFib3OZnrE,1135
1998
1998
  vellum/workflows/utils/names.py,sha256=QtHquoaGqRseu5gg2OcVGI2d_CMcEOvjb9KspwH4C-A,552
1999
1999
  vellum/workflows/utils/pydantic_schema.py,sha256=eR_bBtY-T0pttJP-ARwagSdCOnwPUtiT3cegm2lzDTQ,1310
@@ -2012,8 +2012,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
2012
2012
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2013
2013
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=Boa-_m9ii2Qsa1RvVM-VYniF7zCpzGgEGy-OnPZkrHg,23941
2014
2014
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
2015
- vellum_ai-1.5.1.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2016
- vellum_ai-1.5.1.dist-info/METADATA,sha256=Ptz4NIBFWJ5foDFhe-2vq_L8oNt9HKbtTxgf0qN_Ei4,5547
2017
- vellum_ai-1.5.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2018
- vellum_ai-1.5.1.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2019
- vellum_ai-1.5.1.dist-info/RECORD,,
2015
+ vellum_ai-1.5.2.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2016
+ vellum_ai-1.5.2.dist-info/METADATA,sha256=-qDjvOuDnllUtWah1AMWM9bItegRKH7bZAY9mDwyIQQ,5547
2017
+ vellum_ai-1.5.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2018
+ vellum_ai-1.5.2.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2019
+ vellum_ai-1.5.2.dist-info/RECORD,,
@@ -407,6 +407,51 @@
407
407
  }
408
408
  ]
409
409
  },
410
+ {
411
+ "id": "527a10bc-30b2-4e36-91d6-91c8e865bac8",
412
+ "display_data": {
413
+ "position": {
414
+ "x": 0.0,
415
+ "y": 0.0
416
+ },
417
+ "comment": {
418
+ "value": "\n Used to map over a list of items and execute a Subworkflow on each iteration.\n\n items: List[MapNodeItemType] - The items to map over\n max_concurrency: Optional[int] = None - The maximum number of concurrent subworkflow executions\n subworkflow: Type[\"BaseWorkflow\"] - The Subworkflow to execute\n ",
419
+ "expanded": true
420
+ }
421
+ },
422
+ "base": {
423
+ "name": "BaseAdornmentNode",
424
+ "module": [
425
+ "vellum",
426
+ "workflows",
427
+ "nodes",
428
+ "bases",
429
+ "base_adornment_node"
430
+ ]
431
+ },
432
+ "definition": {
433
+ "name": "MapNode",
434
+ "module": [
435
+ "vellum",
436
+ "workflows",
437
+ "nodes",
438
+ "core",
439
+ "map_node",
440
+ "node"
441
+ ]
442
+ },
443
+ "trigger": {
444
+ "id": "3a702aac-7b68-47d3-92e1-264883e9532c",
445
+ "merge_behavior": "AWAIT_ATTRIBUTES"
446
+ },
447
+ "ports": [
448
+ {
449
+ "id": "0ae85239-a159-48af-b39c-2484eac901df",
450
+ "name": "default",
451
+ "type": "DEFAULT"
452
+ }
453
+ ]
454
+ },
410
455
  {
411
456
  "id": "7426f273-a43d-4448-a2d2-76d0ee0d069c",
412
457
  "display_data": {
@@ -809,25 +854,21 @@
809
854
  }
810
855
  ],
811
856
  "errors": [
812
- {
813
- "node": "MapNode",
814
- "error": "Expected NodeReference items to have an instance"
815
- },
816
857
  {
817
858
  "node": "SubworkflowDeploymentNode",
818
- "error": "headers: {'server': 'gunicorn', 'date': 'Tue, 23 Sep 2025 21:40:16 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '58', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Authentication credentials were not provided.'}"
859
+ "error": "ApiError: headers: {'server': 'gunicorn', 'date': 'Wed, 24 Sep 2025 04:58:53 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '58', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Authentication credentials were not provided.'}"
819
860
  },
820
861
  {
821
862
  "node": "PromptDeploymentNode",
822
- "error": "headers: {'server': 'gunicorn', 'date': 'Tue, 23 Sep 2025 21:40:16 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '58', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Authentication credentials were not provided.'}"
863
+ "error": "ApiError: headers: {'server': 'gunicorn', 'date': 'Wed, 24 Sep 2025 04:58:53 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '58', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Authentication credentials were not provided.'}"
823
864
  },
824
865
  {
825
866
  "node": "SearchNode",
826
- "error": "Expected NodeReference query to have an instance"
867
+ "error": "ValueError: Expected NodeReference query to have an instance"
827
868
  },
828
869
  {
829
870
  "node": "TemplatingNode",
830
- "error": "TemplatingNode.Outputs.result"
871
+ "error": "KeyError: TemplatingNode.Outputs.result"
831
872
  }
832
873
  ]
833
874
  }
@@ -70,7 +70,7 @@ def main() -> None:
70
70
  display_instance = display_class()
71
71
  display_instance.serialize(display_context)
72
72
  except Exception as e:
73
- errors.append({"node": node_class.__name__, "error": str(e)})
73
+ errors.append({"node": node_class.__name__, "error": f"{e.__class__.__name__}: {str(e)}"})
74
74
 
75
75
  result = {"nodes": successful_nodes, "errors": errors}
76
76
 
@@ -1,7 +1,8 @@
1
1
  from uuid import UUID
2
- from typing import Generic, Optional, TypeVar, cast
2
+ from typing import Generic, Optional, Type, TypeVar, cast
3
3
 
4
4
  from vellum.workflows.nodes import MapNode
5
+ from vellum.workflows.state.base import BaseState
5
6
  from vellum.workflows.types.core import JsonObject
6
7
  from vellum.workflows.workflows.base import BaseWorkflow
7
8
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
@@ -22,12 +23,19 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
22
23
  node = self._node
23
24
  node_id = self.node_id
24
25
 
25
- subworkflow = cast(type[BaseWorkflow], raise_if_descriptor(node.subworkflow))
26
+ subworkflow_value = raise_if_descriptor(node.subworkflow)
27
+ subworkflow = (
28
+ cast(type[BaseWorkflow], subworkflow_value)
29
+ if subworkflow_value is not None
30
+ else self._default_workflow_class()
31
+ )
32
+
33
+ items = raise_if_descriptor(node.items)
26
34
 
27
35
  items_node_input = create_node_input(
28
36
  node_id=node_id,
29
37
  input_name="items",
30
- value=node.items,
38
+ value=items or [],
31
39
  display_context=display_context,
32
40
  input_id=self.node_input_ids_by_name.get("items"),
33
41
  )
@@ -81,3 +89,9 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
81
89
  },
82
90
  **self.serialize_generic_fields(display_context),
83
91
  }
92
+
93
+ def _default_workflow_class(self) -> Type[BaseWorkflow]:
94
+ class MapNodeSubworkflow(BaseWorkflow[MapNode.SubworkflowInputs, BaseState]):
95
+ pass
96
+
97
+ return MapNodeSubworkflow