vellum-ai 1.0.8__py3-none-any.whl → 1.0.9__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/client/resources/workflows/client.py +8 -0
- vellum/client/types/document_document_to_document_index.py +1 -1
- vellum/client/types/organization_read.py +2 -0
- vellum/client/types/slim_document_document_to_document_index.py +1 -1
- vellum/plugins/pydantic.py +9 -2
- vellum/workflows/integrations/composio_service.py +12 -0
- vellum/workflows/integrations/mcp_service.py +245 -0
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +17 -1
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +2 -0
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py +3 -1
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +85 -17
- vellum/workflows/types/core.py +5 -1
- {vellum_ai-1.0.8.dist-info → vellum_ai-1.0.9.dist-info}/METADATA +1 -1
- {vellum_ai-1.0.8.dist-info → vellum_ai-1.0.9.dist-info}/RECORD +22 -21
- vellum_cli/push.py +4 -1
- vellum_cli/tests/test_push.py +4 -6
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +5 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +5 -0
- {vellum_ai-1.0.8.dist-info → vellum_ai-1.0.9.dist-info}/LICENSE +0 -0
- {vellum_ai-1.0.8.dist-info → vellum_ai-1.0.9.dist-info}/WHEEL +0 -0
- {vellum_ai-1.0.8.dist-info → vellum_ai-1.0.9.dist-info}/entry_points.txt +0 -0
@@ -25,10 +25,10 @@ class BaseClientWrapper:
|
|
25
25
|
|
26
26
|
def get_headers(self) -> typing.Dict[str, str]:
|
27
27
|
headers: typing.Dict[str, str] = {
|
28
|
-
"User-Agent": "vellum-ai/1.0.
|
28
|
+
"User-Agent": "vellum-ai/1.0.9",
|
29
29
|
"X-Fern-Language": "Python",
|
30
30
|
"X-Fern-SDK-Name": "vellum-ai",
|
31
|
-
"X-Fern-SDK-Version": "1.0.
|
31
|
+
"X-Fern-SDK-Version": "1.0.9",
|
32
32
|
}
|
33
33
|
if self._api_version is not None:
|
34
34
|
headers["X-API-Version"] = self._api_version
|
@@ -27,6 +27,7 @@ class WorkflowsClient:
|
|
27
27
|
id: str,
|
28
28
|
*,
|
29
29
|
exclude_code: typing.Optional[bool] = None,
|
30
|
+
exclude_display: typing.Optional[bool] = None,
|
30
31
|
include_json: typing.Optional[bool] = None,
|
31
32
|
include_sandbox: typing.Optional[bool] = None,
|
32
33
|
strict: typing.Optional[bool] = None,
|
@@ -40,6 +41,8 @@ class WorkflowsClient:
|
|
40
41
|
|
41
42
|
exclude_code : typing.Optional[bool]
|
42
43
|
|
44
|
+
exclude_display : typing.Optional[bool]
|
45
|
+
|
43
46
|
include_json : typing.Optional[bool]
|
44
47
|
|
45
48
|
include_sandbox : typing.Optional[bool]
|
@@ -60,6 +63,7 @@ class WorkflowsClient:
|
|
60
63
|
method="GET",
|
61
64
|
params={
|
62
65
|
"exclude_code": exclude_code,
|
66
|
+
"exclude_display": exclude_display,
|
63
67
|
"include_json": include_json,
|
64
68
|
"include_sandbox": include_sandbox,
|
65
69
|
"strict": strict,
|
@@ -165,6 +169,7 @@ class AsyncWorkflowsClient:
|
|
165
169
|
id: str,
|
166
170
|
*,
|
167
171
|
exclude_code: typing.Optional[bool] = None,
|
172
|
+
exclude_display: typing.Optional[bool] = None,
|
168
173
|
include_json: typing.Optional[bool] = None,
|
169
174
|
include_sandbox: typing.Optional[bool] = None,
|
170
175
|
strict: typing.Optional[bool] = None,
|
@@ -178,6 +183,8 @@ class AsyncWorkflowsClient:
|
|
178
183
|
|
179
184
|
exclude_code : typing.Optional[bool]
|
180
185
|
|
186
|
+
exclude_display : typing.Optional[bool]
|
187
|
+
|
181
188
|
include_json : typing.Optional[bool]
|
182
189
|
|
183
190
|
include_sandbox : typing.Optional[bool]
|
@@ -198,6 +205,7 @@ class AsyncWorkflowsClient:
|
|
198
205
|
method="GET",
|
199
206
|
params={
|
200
207
|
"exclude_code": exclude_code,
|
208
|
+
"exclude_display": exclude_display,
|
201
209
|
"include_json": include_json,
|
202
210
|
"include_sandbox": include_sandbox,
|
203
211
|
"strict": strict,
|
@@ -22,7 +22,7 @@ class DocumentDocumentToDocumentIndex(UniversalBaseModel):
|
|
22
22
|
Vellum-generated ID that uniquely identifies the environment index this document is included in.
|
23
23
|
"""
|
24
24
|
|
25
|
-
document_index_id: str = pydantic.Field()
|
25
|
+
document_index_id: typing.Optional[str] = pydantic.Field(default=None)
|
26
26
|
"""
|
27
27
|
Vellum-generated ID that uniquely identifies the index this document is included in.
|
28
28
|
"""
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from ..core.pydantic_utilities import UniversalBaseModel
|
4
4
|
import typing
|
5
|
+
import datetime as dt
|
5
6
|
from .new_member_join_behavior_enum import NewMemberJoinBehaviorEnum
|
6
7
|
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
7
8
|
import pydantic
|
@@ -10,6 +11,7 @@ import pydantic
|
|
10
11
|
class OrganizationRead(UniversalBaseModel):
|
11
12
|
id: str
|
12
13
|
name: str
|
14
|
+
created: typing.Optional[dt.datetime] = None
|
13
15
|
allow_staff_access: typing.Optional[bool] = None
|
14
16
|
new_member_join_behavior: NewMemberJoinBehaviorEnum
|
15
17
|
limit_config: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
|
@@ -22,7 +22,7 @@ class SlimDocumentDocumentToDocumentIndex(UniversalBaseModel):
|
|
22
22
|
Vellum-generated ID that uniquely identifies the environment index this document is included in.
|
23
23
|
"""
|
24
24
|
|
25
|
-
document_index_id: str = pydantic.Field()
|
25
|
+
document_index_id: typing.Optional[str] = pydantic.Field(default=None)
|
26
26
|
"""
|
27
27
|
Vellum-generated ID that uniquely identifies the index this document is included in.
|
28
28
|
"""
|
vellum/plugins/pydantic.py
CHANGED
@@ -28,9 +28,12 @@ def import_base_descriptor():
|
|
28
28
|
"""
|
29
29
|
We have to avoid importing from vellum.* in this file because it will cause a circular import.
|
30
30
|
"""
|
31
|
-
|
31
|
+
try:
|
32
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
32
33
|
|
33
|
-
|
34
|
+
return BaseDescriptor
|
35
|
+
except Exception:
|
36
|
+
return None
|
34
37
|
|
35
38
|
|
36
39
|
# https://docs.pydantic.dev/2.8/concepts/plugins/#build-a-plugin
|
@@ -60,6 +63,10 @@ class OnValidatePython(ValidatePythonHandlerProtocol):
|
|
60
63
|
self.tracked_descriptors = {}
|
61
64
|
BaseDescriptor = import_base_descriptor()
|
62
65
|
|
66
|
+
# If BaseDescriptor import failed, skip descriptor processing
|
67
|
+
if BaseDescriptor is None:
|
68
|
+
return
|
69
|
+
|
63
70
|
for key, value in input.items():
|
64
71
|
field_info = model_fields.get(key)
|
65
72
|
if isinstance(value, BaseDescriptor) and (
|
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional
|
|
5
5
|
|
6
6
|
import requests
|
7
7
|
|
8
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
8
9
|
from vellum.workflows.exceptions import NodeException
|
9
10
|
|
10
11
|
logger = logging.getLogger(__name__)
|
@@ -72,6 +73,17 @@ class ComposioService:
|
|
72
73
|
|
73
74
|
response.raise_for_status()
|
74
75
|
return response.json()
|
76
|
+
except requests.exceptions.HTTPError as e:
|
77
|
+
if e.response.status_code == 401:
|
78
|
+
raise NodeException(
|
79
|
+
message="Failed to authorize Composio request. Make sure to define a COMPOSIO_API_KEY",
|
80
|
+
code=WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE,
|
81
|
+
)
|
82
|
+
else:
|
83
|
+
response_text = e.response.text if e.response else "No response"
|
84
|
+
raise NodeException(
|
85
|
+
f"Composio API request failed with status {e.response.status_code}: {response_text}"
|
86
|
+
)
|
75
87
|
except Exception as e:
|
76
88
|
raise NodeException(f"Composio API request failed: {e}")
|
77
89
|
|
@@ -0,0 +1,245 @@
|
|
1
|
+
import asyncio
|
2
|
+
import json
|
3
|
+
import logging
|
4
|
+
from typing import Any, Dict, List, Optional
|
5
|
+
|
6
|
+
import httpx
|
7
|
+
|
8
|
+
from vellum.workflows.constants import AuthorizationType
|
9
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
10
|
+
from vellum.workflows.exceptions import NodeException
|
11
|
+
from vellum.workflows.types.core import VellumSecret
|
12
|
+
from vellum.workflows.types.definition import MCPServer, MCPToolDefinition
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class MCPHttpClient:
|
18
|
+
"""
|
19
|
+
Direct HTTP implementation for MCP (Model Context Protocol) client
|
20
|
+
without using the official Python SDK.
|
21
|
+
|
22
|
+
Supports Streamable HTTP transport using httpx.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, server_url: str, headers: Dict[str, str], session_timeout: int = 30):
|
26
|
+
"""
|
27
|
+
Initialize MCP HTTP client.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
server_url: The MCP server endpoint URL (e.g., "https://example.com/mcp")
|
31
|
+
headers: Authentication headers
|
32
|
+
session_timeout: Timeout for HTTP requests in seconds
|
33
|
+
"""
|
34
|
+
self.server_url = server_url.rstrip("/")
|
35
|
+
self.headers = headers
|
36
|
+
self.session_timeout = session_timeout
|
37
|
+
self.session_id: Optional[str] = None
|
38
|
+
self.request_id = 0
|
39
|
+
self._client: Optional[httpx.AsyncClient] = None
|
40
|
+
|
41
|
+
async def __aenter__(self):
|
42
|
+
"""Async context manager entry."""
|
43
|
+
self._client = httpx.AsyncClient(timeout=httpx.Timeout(self.session_timeout), headers=self.headers)
|
44
|
+
return self
|
45
|
+
|
46
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
47
|
+
"""Async context manager exit."""
|
48
|
+
if self._client:
|
49
|
+
await self._client.aclose()
|
50
|
+
|
51
|
+
def _next_request_id(self) -> int:
|
52
|
+
"""Generate next request ID."""
|
53
|
+
self.request_id += 1
|
54
|
+
return self.request_id
|
55
|
+
|
56
|
+
async def _send_request(self, method: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
57
|
+
"""
|
58
|
+
Send a JSON-RPC request to the MCP server.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
method: The JSON-RPC method name
|
62
|
+
params: Optional parameters for the method
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
The JSON-RPC response
|
66
|
+
"""
|
67
|
+
if not self._client:
|
68
|
+
raise RuntimeError("Client session not initialized. Use 'async with' context manager.")
|
69
|
+
|
70
|
+
# Prepare JSON-RPC request
|
71
|
+
request_data = {"jsonrpc": "2.0", "id": self._next_request_id(), "method": method, "params": params or {}}
|
72
|
+
|
73
|
+
# Prepare headers
|
74
|
+
headers = {
|
75
|
+
"Content-Type": "application/json",
|
76
|
+
"Accept": "application/json",
|
77
|
+
}
|
78
|
+
|
79
|
+
# Include session ID if we have one
|
80
|
+
if self.session_id:
|
81
|
+
headers["Mcp-Session-Id"] = self.session_id
|
82
|
+
|
83
|
+
logger.debug(f"Sending request: {json.dumps(request_data, indent=2)}")
|
84
|
+
|
85
|
+
# Send POST request
|
86
|
+
response = await self._client.post(self.server_url, json=request_data, headers=headers)
|
87
|
+
|
88
|
+
# Check for session ID in response headers
|
89
|
+
if "Mcp-Session-Id" in response.headers:
|
90
|
+
self.session_id = response.headers["Mcp-Session-Id"]
|
91
|
+
logger.debug(f"Received session ID: {self.session_id}")
|
92
|
+
|
93
|
+
# Handle JSON response
|
94
|
+
response_data = response.json()
|
95
|
+
logger.debug(f"Received response: {json.dumps(response_data, indent=2)}")
|
96
|
+
|
97
|
+
if "error" in response_data:
|
98
|
+
raise Exception(f"MCP Error: {response_data['error']}")
|
99
|
+
|
100
|
+
return response_data
|
101
|
+
|
102
|
+
async def initialize(self, client_info: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
103
|
+
"""
|
104
|
+
Initialize the MCP connection.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
client_info: Optional client information
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
Server capabilities and information
|
111
|
+
"""
|
112
|
+
params = {
|
113
|
+
"protocolVersion": "2025-06-18",
|
114
|
+
"capabilities": {},
|
115
|
+
"clientInfo": client_info or {"name": "vellum-mcp-client", "version": "1.0.0"},
|
116
|
+
}
|
117
|
+
|
118
|
+
response = await self._send_request("initialize", params)
|
119
|
+
return response.get("result", {})
|
120
|
+
|
121
|
+
async def list_tools(self) -> List[Dict[str, Any]]:
|
122
|
+
"""
|
123
|
+
Get list of available tools from the server.
|
124
|
+
|
125
|
+
Returns:
|
126
|
+
List of tool definitions
|
127
|
+
"""
|
128
|
+
response = await self._send_request("tools/list")
|
129
|
+
return response.get("result", {}).get("tools", [])
|
130
|
+
|
131
|
+
async def call_tool(self, name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
132
|
+
"""
|
133
|
+
Call a tool on the server.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
name: Tool name
|
137
|
+
arguments: Tool arguments
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
Tool execution result
|
141
|
+
"""
|
142
|
+
params = {"name": name, "arguments": arguments}
|
143
|
+
|
144
|
+
response = await self._send_request("tools/call", params)
|
145
|
+
return response.get("result", {})
|
146
|
+
|
147
|
+
|
148
|
+
class MCPService:
|
149
|
+
def _get_auth_headers(self, server: MCPServer) -> Dict[str, str]:
|
150
|
+
headers = {}
|
151
|
+
if server.authorization_type == AuthorizationType.BEARER_TOKEN:
|
152
|
+
token = server.bearer_token_value
|
153
|
+
if not token:
|
154
|
+
raise NodeException(
|
155
|
+
"Bearer token is required for BEARER_TOKEN authorization type",
|
156
|
+
code=WorkflowErrorCode.INVALID_INPUTS,
|
157
|
+
)
|
158
|
+
|
159
|
+
headers["Authorization"] = f"Bearer {token}"
|
160
|
+
|
161
|
+
elif server.authorization_type == AuthorizationType.API_KEY:
|
162
|
+
key = server.api_key_header_key
|
163
|
+
value = server.api_key_header_value
|
164
|
+
if not key or not value:
|
165
|
+
raise NodeException(
|
166
|
+
"API key header key and value are required for API_KEY authorization type",
|
167
|
+
code=WorkflowErrorCode.INVALID_INPUTS,
|
168
|
+
)
|
169
|
+
if isinstance(value, VellumSecret):
|
170
|
+
headers[key] = value.name
|
171
|
+
elif isinstance(value, str):
|
172
|
+
headers[key] = value
|
173
|
+
|
174
|
+
return headers
|
175
|
+
|
176
|
+
async def _execute_mcp_call(self, server: MCPServer, operation: str, **kwargs) -> Any:
|
177
|
+
"""Execute an MCP operation using direct HTTP calls."""
|
178
|
+
headers = self._get_auth_headers(server)
|
179
|
+
|
180
|
+
try:
|
181
|
+
async with MCPHttpClient(server.url, headers) as client:
|
182
|
+
await client.initialize()
|
183
|
+
|
184
|
+
if operation == "list_tools":
|
185
|
+
return await client.list_tools()
|
186
|
+
elif operation == "call_tool":
|
187
|
+
return await client.call_tool(
|
188
|
+
name=kwargs["name"],
|
189
|
+
arguments=kwargs["arguments"],
|
190
|
+
)
|
191
|
+
else:
|
192
|
+
raise ValueError(f"Unknown MCP operation: {operation}")
|
193
|
+
|
194
|
+
except Exception as e:
|
195
|
+
logger.error(f"Error executing MCP operation {operation}: {e}")
|
196
|
+
raise NodeException(
|
197
|
+
message=f"Error executing MCP operation '{operation}': {str(e)}",
|
198
|
+
code=WorkflowErrorCode.NODE_EXECUTION,
|
199
|
+
)
|
200
|
+
|
201
|
+
def list_tools(self, server: MCPServer) -> List[Dict[str, Any]]:
|
202
|
+
"""List available tools from an MCP server."""
|
203
|
+
try:
|
204
|
+
tools = asyncio.run(self._execute_mcp_call(server, "list_tools"))
|
205
|
+
return tools
|
206
|
+
except Exception as e:
|
207
|
+
logger.warning(f"Failed to list tools from MCP server '{server.name}': {e}")
|
208
|
+
return []
|
209
|
+
|
210
|
+
def execute_tool(self, tool_def: MCPToolDefinition, arguments: Dict[str, Any]) -> Any:
|
211
|
+
"""Execute a tool on an MCP server."""
|
212
|
+
try:
|
213
|
+
result = asyncio.run(
|
214
|
+
self._execute_mcp_call(
|
215
|
+
tool_def.server,
|
216
|
+
"call_tool",
|
217
|
+
name=tool_def.name,
|
218
|
+
arguments=arguments,
|
219
|
+
)
|
220
|
+
)
|
221
|
+
return result
|
222
|
+
except Exception as e:
|
223
|
+
logger.error(f"Error executing MCP tool '{tool_def.name}': {e}")
|
224
|
+
raise NodeException(
|
225
|
+
message=f"Error executing MCP tool '{tool_def.name}': {str(e)}",
|
226
|
+
code=WorkflowErrorCode.NODE_EXECUTION,
|
227
|
+
)
|
228
|
+
|
229
|
+
def hydrate_tool_definitions(self, server_def: MCPServer) -> List[MCPToolDefinition]:
|
230
|
+
"""Hydrate an MCPToolDefinition with detailed information from the MCP server."""
|
231
|
+
try:
|
232
|
+
tools = self.list_tools(server_def)
|
233
|
+
|
234
|
+
return [
|
235
|
+
MCPToolDefinition(
|
236
|
+
name=tool["name"],
|
237
|
+
server=server_def,
|
238
|
+
description=tool["description"],
|
239
|
+
parameters=tool["inputSchema"],
|
240
|
+
)
|
241
|
+
for tool in tools
|
242
|
+
]
|
243
|
+
except Exception as e:
|
244
|
+
logger.warning(f"Failed to hydrate MCP server '{server_def.name}': {e}")
|
245
|
+
return []
|
@@ -15,10 +15,12 @@ from vellum.workflows.nodes.displayable.tool_calling_node.utils import (
|
|
15
15
|
create_function_node,
|
16
16
|
create_tool_router_node,
|
17
17
|
get_function_name,
|
18
|
+
hydrate_mcp_tool_definitions,
|
18
19
|
)
|
19
20
|
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
20
21
|
from vellum.workflows.state.context import WorkflowContext
|
21
|
-
from vellum.workflows.types.core import EntityInputsInterface, Tool
|
22
|
+
from vellum.workflows.types.core import EntityInputsInterface, Tool, ToolSource
|
23
|
+
from vellum.workflows.types.definition import MCPServer
|
22
24
|
from vellum.workflows.workflows.event_filters import all_workflow_event_filter
|
23
25
|
|
24
26
|
|
@@ -30,6 +32,7 @@ class ToolCallingNode(BaseNode):
|
|
30
32
|
ml_model: str - The model to use for tool calling (e.g., "gpt-4o-mini")
|
31
33
|
blocks: List[PromptBlock] - The prompt blocks to use (same format as InlinePromptNode)
|
32
34
|
functions: List[Tool] - The functions that can be called
|
35
|
+
tool_sources: List[ToolSource] - The tool sources that can be called
|
33
36
|
prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values
|
34
37
|
parameters: PromptParameters - The parameters for the Prompt
|
35
38
|
max_prompt_iterations: Optional[int] - Maximum number of prompt iterations before stopping
|
@@ -38,6 +41,7 @@ class ToolCallingNode(BaseNode):
|
|
38
41
|
ml_model: ClassVar[str] = "gpt-4o-mini"
|
39
42
|
blocks: ClassVar[List[Union[PromptBlock, Dict[str, Any]]]] = []
|
40
43
|
functions: ClassVar[List[Tool]] = []
|
44
|
+
tool_sources: ClassVar[List[ToolSource]] = []
|
41
45
|
prompt_inputs: ClassVar[Optional[EntityInputsInterface]] = None
|
42
46
|
parameters: PromptParameters = DEFAULT_PROMPT_PARAMETERS
|
43
47
|
max_prompt_iterations: ClassVar[Optional[int]] = 5
|
@@ -134,6 +138,7 @@ class ToolCallingNode(BaseNode):
|
|
134
138
|
ml_model=self.ml_model,
|
135
139
|
blocks=self.blocks,
|
136
140
|
functions=self.functions,
|
141
|
+
tool_sources=self.tool_sources,
|
137
142
|
prompt_inputs=self.prompt_inputs,
|
138
143
|
parameters=self.parameters,
|
139
144
|
max_prompt_iterations=self.max_prompt_iterations,
|
@@ -148,6 +153,17 @@ class ToolCallingNode(BaseNode):
|
|
148
153
|
tool_router_node=self.tool_router_node,
|
149
154
|
)
|
150
155
|
|
156
|
+
for tool_source in self.tool_sources:
|
157
|
+
if isinstance(tool_source, MCPServer):
|
158
|
+
tool_definitions = hydrate_mcp_tool_definitions(tool_source)
|
159
|
+
for tool_definition in tool_definitions:
|
160
|
+
function_name = get_function_name(tool_definition)
|
161
|
+
|
162
|
+
self._function_nodes[function_name] = create_function_node(
|
163
|
+
function=tool_definition,
|
164
|
+
tool_router_node=self.tool_router_node,
|
165
|
+
)
|
166
|
+
|
151
167
|
graph_set = set()
|
152
168
|
|
153
169
|
# Add connections from ports of router to function nodes and back to router
|
@@ -39,6 +39,7 @@ def test_port_condition_match_function_name():
|
|
39
39
|
ml_model="test-model",
|
40
40
|
blocks=[],
|
41
41
|
functions=[first_function, second_function],
|
42
|
+
tool_sources=[],
|
42
43
|
prompt_inputs=None,
|
43
44
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
44
45
|
)
|
@@ -97,6 +98,7 @@ def test_tool_calling_node_inline_workflow_context():
|
|
97
98
|
ml_model="test-model",
|
98
99
|
blocks=[],
|
99
100
|
functions=[MyWorkflow],
|
101
|
+
tool_sources=[],
|
100
102
|
prompt_inputs=None,
|
101
103
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
102
104
|
)
|
@@ -79,7 +79,7 @@ def test_get_function_name_mcp_tool_definition():
|
|
79
79
|
|
80
80
|
result = get_function_name(mcp_tool)
|
81
81
|
|
82
|
-
assert result == "
|
82
|
+
assert result == "github__create_repository"
|
83
83
|
|
84
84
|
|
85
85
|
@pytest.mark.parametrize(
|
@@ -106,6 +106,7 @@ def test_create_tool_router_node_max_prompt_iterations(vellum_adhoc_prompt_clien
|
|
106
106
|
ml_model="gpt-4o-mini",
|
107
107
|
blocks=[],
|
108
108
|
functions=[],
|
109
|
+
tool_sources=[],
|
109
110
|
prompt_inputs=None,
|
110
111
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
111
112
|
max_prompt_iterations=None,
|
@@ -165,6 +166,7 @@ def test_create_tool_router_node_chat_history_block_dict(vellum_adhoc_prompt_cli
|
|
165
166
|
ml_model="gpt-4o-mini",
|
166
167
|
blocks=blocks, # type: ignore
|
167
168
|
functions=[],
|
169
|
+
tool_sources=[],
|
168
170
|
prompt_inputs=None,
|
169
171
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
170
172
|
)
|
@@ -17,6 +17,7 @@ from vellum.workflows.exceptions import NodeException
|
|
17
17
|
from vellum.workflows.expressions.concat import ConcatExpression
|
18
18
|
from vellum.workflows.inputs import BaseInputs
|
19
19
|
from vellum.workflows.integrations.composio_service import ComposioService
|
20
|
+
from vellum.workflows.integrations.mcp_service import MCPService
|
20
21
|
from vellum.workflows.nodes.bases import BaseNode
|
21
22
|
from vellum.workflows.nodes.core.inline_subworkflow_node.node import InlineSubworkflowNode
|
22
23
|
from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
|
@@ -27,8 +28,8 @@ from vellum.workflows.ports.port import Port
|
|
27
28
|
from vellum.workflows.references.lazy import LazyReference
|
28
29
|
from vellum.workflows.state import BaseState
|
29
30
|
from vellum.workflows.state.encoder import DefaultStateEncoder
|
30
|
-
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior, Tool
|
31
|
-
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPToolDefinition
|
31
|
+
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior, Tool, ToolSource
|
32
|
+
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPServer, MCPToolDefinition
|
32
33
|
from vellum.workflows.types.generics import is_workflow_class
|
33
34
|
|
34
35
|
CHAT_HISTORY_VARIABLE = "chat_history"
|
@@ -196,6 +197,29 @@ class ComposioNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
196
197
|
yield from []
|
197
198
|
|
198
199
|
|
200
|
+
class MCPNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
201
|
+
"""Node that executes an MCP tool with function call output."""
|
202
|
+
|
203
|
+
mcp_tool: MCPToolDefinition
|
204
|
+
|
205
|
+
def run(self) -> Iterator[BaseOutput]:
|
206
|
+
arguments = self._extract_function_arguments()
|
207
|
+
|
208
|
+
try:
|
209
|
+
mcp_service = MCPService()
|
210
|
+
result = mcp_service.execute_tool(tool_def=self.mcp_tool, arguments=arguments)
|
211
|
+
except Exception as e:
|
212
|
+
raise NodeException(
|
213
|
+
message=f"Error executing MCP tool '{self.mcp_tool.name}': {str(e)}",
|
214
|
+
code=WorkflowErrorCode.NODE_EXECUTION,
|
215
|
+
)
|
216
|
+
|
217
|
+
# Add result to chat history
|
218
|
+
self._add_function_result_to_chat_history(result, self.state)
|
219
|
+
|
220
|
+
yield from []
|
221
|
+
|
222
|
+
|
199
223
|
def _hydrate_composio_tool_definition(tool_def: ComposioToolDefinition) -> ComposioToolDefinition:
|
200
224
|
"""Hydrate a ComposioToolDefinition with detailed information from the Composio API.
|
201
225
|
|
@@ -233,19 +257,53 @@ def _hydrate_composio_tool_definition(tool_def: ComposioToolDefinition) -> Compo
|
|
233
257
|
return tool_def
|
234
258
|
|
235
259
|
|
260
|
+
def hydrate_mcp_tool_definitions(server_def: MCPServer) -> List[MCPToolDefinition]:
|
261
|
+
"""Hydrate an MCPToolDefinition with detailed information from the MCP server.
|
262
|
+
|
263
|
+
We do tool discovery on the MCP server to get the tool definitions.
|
264
|
+
|
265
|
+
Args:
|
266
|
+
tool_def: The basic MCPToolDefinition to enhance
|
267
|
+
|
268
|
+
Returns:
|
269
|
+
MCPToolDefinition with detailed parameters and description
|
270
|
+
"""
|
271
|
+
try:
|
272
|
+
mcp_service = MCPService()
|
273
|
+
return mcp_service.hydrate_tool_definitions(server_def)
|
274
|
+
except Exception as e:
|
275
|
+
# If hydration fails, log and return original
|
276
|
+
logger.warning(f"Failed to enhance MCP server '{server_def.name}': {e}")
|
277
|
+
return []
|
278
|
+
|
279
|
+
|
236
280
|
def create_tool_router_node(
|
237
281
|
ml_model: str,
|
238
282
|
blocks: List[Union[PromptBlock, Dict[str, Any]]],
|
239
283
|
functions: List[Tool],
|
284
|
+
tool_sources: List[ToolSource],
|
240
285
|
prompt_inputs: Optional[EntityInputsInterface],
|
241
286
|
parameters: PromptParameters,
|
242
287
|
max_prompt_iterations: Optional[int] = None,
|
243
288
|
) -> Type[ToolRouterNode]:
|
244
|
-
if functions and len(functions) > 0:
|
289
|
+
if functions and len(functions) > 0 or tool_sources and len(tool_sources) > 0:
|
245
290
|
# Create dynamic ports and convert functions in a single loop
|
246
291
|
Ports = type("Ports", (), {})
|
247
292
|
prompt_functions: List[Union[Tool, FunctionDefinition]] = []
|
248
293
|
|
294
|
+
# Avoid using lambda to capture function_name
|
295
|
+
# lambda will capture the function_name by reference,
|
296
|
+
# and if the function_name is changed, the port_condition will also change.
|
297
|
+
def create_port_condition(fn_name):
|
298
|
+
return Port.on_if(
|
299
|
+
LazyReference(
|
300
|
+
lambda: (
|
301
|
+
node.Outputs.results[0]["type"].equals("FUNCTION_CALL")
|
302
|
+
& node.Outputs.results[0]["value"]["name"].equals(fn_name)
|
303
|
+
)
|
304
|
+
)
|
305
|
+
)
|
306
|
+
|
249
307
|
for function in functions:
|
250
308
|
if isinstance(function, ComposioToolDefinition):
|
251
309
|
# Get Composio tool details and hydrate the function definition
|
@@ -262,21 +320,22 @@ def create_tool_router_node(
|
|
262
320
|
|
263
321
|
# Create port for this function (using original function for get_function_name)
|
264
322
|
function_name = get_function_name(function)
|
323
|
+
port = create_port_condition(function_name)
|
324
|
+
setattr(Ports, function_name, port)
|
265
325
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
326
|
+
for server in tool_sources:
|
327
|
+
tool_functions = hydrate_mcp_tool_definitions(server)
|
328
|
+
for tool_function in tool_functions:
|
329
|
+
name = get_function_name(tool_function)
|
330
|
+
prompt_functions.append(
|
331
|
+
FunctionDefinition(
|
332
|
+
name=name,
|
333
|
+
description=tool_function.description,
|
334
|
+
parameters=tool_function.parameters,
|
274
335
|
)
|
275
336
|
)
|
276
|
-
|
277
|
-
|
278
|
-
port = Port.on_if(port_condition)
|
279
|
-
setattr(Ports, function_name, port)
|
337
|
+
port = create_port_condition(name)
|
338
|
+
setattr(Ports, name, port)
|
280
339
|
|
281
340
|
# Add the else port for when no function conditions match
|
282
341
|
setattr(Ports, "default", Port.on_else())
|
@@ -380,7 +439,16 @@ def create_function_node(
|
|
380
439
|
)
|
381
440
|
return node
|
382
441
|
elif isinstance(function, MCPToolDefinition):
|
383
|
-
|
442
|
+
node = type(
|
443
|
+
f"MCPNode_{function.name}",
|
444
|
+
(MCPNode,),
|
445
|
+
{
|
446
|
+
"mcp_tool": function,
|
447
|
+
"function_call_output": tool_router_node.Outputs.results,
|
448
|
+
"__module__": __name__,
|
449
|
+
},
|
450
|
+
)
|
451
|
+
return node
|
384
452
|
elif is_workflow_class(function):
|
385
453
|
node = type(
|
386
454
|
f"DynamicInlineSubworkflowNode_{function.__name__}",
|
@@ -413,6 +481,6 @@ def get_function_name(function: Tool) -> str:
|
|
413
481
|
elif isinstance(function, ComposioToolDefinition):
|
414
482
|
return function.name
|
415
483
|
elif isinstance(function, MCPToolDefinition):
|
416
|
-
return function.name
|
484
|
+
return f"{function.server.name}__{function.name}"
|
417
485
|
else:
|
418
486
|
return snake_case(function.__name__)
|
vellum/workflows/types/core.py
CHANGED
@@ -13,7 +13,7 @@ from typing import ( # type: ignore[attr-defined]
|
|
13
13
|
)
|
14
14
|
|
15
15
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
16
|
-
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPToolDefinition
|
16
|
+
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPServer, MCPToolDefinition
|
17
17
|
|
18
18
|
if TYPE_CHECKING:
|
19
19
|
from vellum.workflows.workflows.base import BaseWorkflow
|
@@ -41,6 +41,7 @@ class MergeBehavior(Enum):
|
|
41
41
|
AWAIT_ALL = "AWAIT_ALL"
|
42
42
|
AWAIT_ANY = "AWAIT_ANY"
|
43
43
|
AWAIT_ATTRIBUTES = "AWAIT_ATTRIBUTES"
|
44
|
+
CUSTOM = "CUSTOM"
|
44
45
|
|
45
46
|
|
46
47
|
class ConditionType(Enum):
|
@@ -51,3 +52,6 @@ class ConditionType(Enum):
|
|
51
52
|
|
52
53
|
# Type alias for functions that can be called in tool calling nodes
|
53
54
|
Tool = Union[Callable[..., Any], DeploymentDefinition, Type["BaseWorkflow"], ComposioToolDefinition, MCPToolDefinition]
|
55
|
+
|
56
|
+
# Type alias for sources that provide tools to tool calling nodes
|
57
|
+
ToolSource = Union[MCPServer]
|
@@ -8,7 +8,7 @@ vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
|
|
8
8
|
vellum_cli/logger.py,sha256=dcM_OmgqXLo93vDYswO5ylyUQQcTfnA5GTd5tbIt3wM,1446
|
9
9
|
vellum_cli/ping.py,sha256=p_BCCRjgPhng6JktuECtkDQLbhopt6JpmrtGoLnLJT8,1161
|
10
10
|
vellum_cli/pull.py,sha256=udYyPlJ6VKDdh78rApNJOZgxHl82fcV6iGnRPSdX1LY,14750
|
11
|
-
vellum_cli/push.py,sha256=
|
11
|
+
vellum_cli/push.py,sha256=5sGkWmwnuPI9mYlBFZO4Wc0WcErZAnFNXZYJByiAJGw,11620
|
12
12
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
vellum_cli/tests/conftest.py,sha256=wx3PlJjVB0HRf5dr2b_idOIw27WPPl0J0FNbhIJJaVk,1689
|
14
14
|
vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
|
@@ -18,7 +18,7 @@ vellum_cli/tests/test_init.py,sha256=8UOc_ThfouR4ja5cCl_URuLk7ohr9JXfCnG4yka1OUQ
|
|
18
18
|
vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
|
19
19
|
vellum_cli/tests/test_ping.py,sha256=b3aQLd-N59_8w2rRiWqwpB1rlHaKEYVbAj1Y3hi7A-g,2605
|
20
20
|
vellum_cli/tests/test_pull.py,sha256=hxMbW_j0weDDrkzVGpvLpFcwNQdn-fxTv4wBHeYizzc,49904
|
21
|
-
vellum_cli/tests/test_push.py,sha256=
|
21
|
+
vellum_cli/tests/test_push.py,sha256=9whdm-i4d3scJeJ7snGrkCVKcDlZsq8WqH_Nnv9KAYk,40395
|
22
22
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
23
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -95,8 +95,8 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_
|
|
95
95
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=V8b6gKghLlO7PJI8xeNdnfn8aII0W_IFQvSQBQM62UQ,7721
|
96
96
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=hDWtKXmGI1CKhTwTNqpu_d5RkE5n7SolMLtgd87KqTI,3856
|
97
97
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=gonapBCyDDt3qc7U02PCuKyPS8f3YiSAZ7QD86CH1Fw,3794
|
98
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=
|
99
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=
|
98
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=pZJVn7oXyTuKvyLOM6pj00ulSJR2QtVd9h2VwBv4_mg,26522
|
99
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=wKJlecy8kxlOslTlPHRFUb-zFBGpNq6S7eeOrswB9-I,10241
|
100
100
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=mova0sPD3evHiHIN1O0VynxlCp-uOcEIKve5Pd_oCDg,4069
|
101
101
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
|
102
102
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=J4ouI8KxbMfxQP2Zq_9cWMGYgbjCWmKzjCJEtnSJb0I,5829
|
@@ -145,7 +145,7 @@ vellum/client/README.md,sha256=Dle5iytCXxP1pNeNd7uZyhFo0rl7tp7vU7s8gmi10OQ,4863
|
|
145
145
|
vellum/client/__init__.py,sha256=KmkyOgReuTsjmXF3WC_dPQ9QqJgYrB3Sr8_LcSUIQyI,125258
|
146
146
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
147
147
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
148
|
-
vellum/client/core/client_wrapper.py,sha256
|
148
|
+
vellum/client/core/client_wrapper.py,sha256=K6kF1J_NBmrtjJs0H4OqVoVsJgY3ivrRe4FzhceFTis,2383
|
149
149
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
150
150
|
vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
|
151
151
|
vellum/client/core/http_client.py,sha256=cKs2w0ybDBk1wHQf-fTALm_MmvaMe3cZKcYJxqmCxkE,19539
|
@@ -210,7 +210,7 @@ vellum/client/resources/workflow_sandboxes/client.py,sha256=tpIjyuvVAnghmUu_gI9l
|
|
210
210
|
vellum/client/resources/workflow_sandboxes/types/__init__.py,sha256=EaGVRU1w6kJiiHrbZOeEa0c3ggjfgv_jBqsyOkCRWOI,212
|
211
211
|
vellum/client/resources/workflow_sandboxes/types/list_workflow_sandbox_examples_request_tag.py,sha256=TEwWit20W3X-zWPPLAhmUG05UudG9gaBSJ4Q4-rNJws,188
|
212
212
|
vellum/client/resources/workflows/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
|
213
|
-
vellum/client/resources/workflows/client.py,sha256=
|
213
|
+
vellum/client/resources/workflows/client.py,sha256=rU5fJmVebWO6SoK5S9RzJHM0-hucRKnDt24tvG-DwEM,11037
|
214
214
|
vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
|
215
215
|
vellum/client/resources/workspace_secrets/client.py,sha256=l1FOj0f-IB5_oQ7iWiHopFK3lDXBqiaIc9g10W9PHFU,8381
|
216
216
|
vellum/client/resources/workspaces/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
|
@@ -295,7 +295,7 @@ vellum/client/types/deployment_release_tag_read.py,sha256=dUrTOz9LH1gAvC_ktMB_7N
|
|
295
295
|
vellum/client/types/docker_service_token.py,sha256=T0icNHBKsIs6TrEiDRjckM_f37hcF1DMwEE8161tTvY,614
|
296
296
|
vellum/client/types/document_chat_message_content.py,sha256=MiVYuMKtRUaT6_ve1MzihChb10SrOt_0VhpCB0x7hFQ,745
|
297
297
|
vellum/client/types/document_chat_message_content_request.py,sha256=wMzj1SREQUeiSqZhOoLOOQzn6hmO_GDOMICDQ4fEXzs,774
|
298
|
-
vellum/client/types/document_document_to_document_index.py,sha256=
|
298
|
+
vellum/client/types/document_document_to_document_index.py,sha256=Yeb84grNv-T9nI2oFjMwioPHUEH2TLREaqhldWMYwhs,1609
|
299
299
|
vellum/client/types/document_index_chunking.py,sha256=TU0Y7z0Xacm3dhzEDuDIG3ZKJCu3vNURRh3PqEd17mY,356
|
300
300
|
vellum/client/types/document_index_chunking_request.py,sha256=g9BKCsHKg5kzjG7YYeMNQ_5R8TXLeSgumJlMXoSfBcs,435
|
301
301
|
vellum/client/types/document_index_indexing_config.py,sha256=xL1pCzUOkw5sSie1OrBpasE3bVnv0UyZBn7uZztbhbs,781
|
@@ -500,7 +500,7 @@ vellum/client/types/open_ai_vectorizer_text_embedding_3_small.py,sha256=T_-P7qGj
|
|
500
500
|
vellum/client/types/open_ai_vectorizer_text_embedding_3_small_request.py,sha256=-lwNeWj7ExP-JLncUp1Uyd20FxweVIDu-aEnenPB98A,841
|
501
501
|
vellum/client/types/open_ai_vectorizer_text_embedding_ada_002.py,sha256=c4vNlR6lRvUjq-67M06sroDMNMG_qC4JUBqwmKEJQ2I,812
|
502
502
|
vellum/client/types/open_ai_vectorizer_text_embedding_ada_002_request.py,sha256=FdpkkNBGgRwfqFjBwpfH4t2zKIM0pIYminX2iZQUzvY,841
|
503
|
-
vellum/client/types/organization_read.py,sha256=
|
503
|
+
vellum/client/types/organization_read.py,sha256=AMTjqWH6CWXzbPpjoOE9Usb0lsqepC_vBfqKjLxHcik,908
|
504
504
|
vellum/client/types/paginated_container_image_read_list.py,sha256=7lwIgs1q7Z0xDYPGWPnjSNC1kU_peu79CotzaaQfRdA,801
|
505
505
|
vellum/client/types/paginated_deployment_release_tag_read_list.py,sha256=hp7D74CxPY14dEPRZ-fnTCwp63upxkYquL1e74oYXh4,826
|
506
506
|
vellum/client/types/paginated_document_index_read_list.py,sha256=bO7pm3KCZi5LDO17YXgr_lUF9SRdAfMu6wOutX91ANw,797
|
@@ -589,7 +589,7 @@ vellum/client/types/sentence_chunking.py,sha256=guqU3072X4h8Laf6LhTWQ5lpjBpTgoXR
|
|
589
589
|
vellum/client/types/sentence_chunking_request.py,sha256=77gv1fVc9IaTuGGx3O1HB0LF9sXM5pSTWksl8BEmvLU,812
|
590
590
|
vellum/client/types/slim_deployment_read.py,sha256=Gr3wWT1eJKe33MVsW0O0pSIgUF_LE6WFbvFbf5_CVb8,1654
|
591
591
|
vellum/client/types/slim_document.py,sha256=HJiymYPvRxfxhBUkD8epW0hQ2Vt9PQtv398QsRb4DsI,2395
|
592
|
-
vellum/client/types/slim_document_document_to_document_index.py,sha256=
|
592
|
+
vellum/client/types/slim_document_document_to_document_index.py,sha256=0LThZhOgIE2kSmIWH9s59_6pEzGRWpx5qS5BBuWXdLk,1551
|
593
593
|
vellum/client/types/slim_release_review.py,sha256=7DXmD1AVa_Wj7e0qiR7GUN9cSqwkk1JloYmp_3oluQQ,783
|
594
594
|
vellum/client/types/slim_workflow_deployment.py,sha256=Js-ycMFZ8-kNFPsd4bZew9nI_eN2M_58LzDHeCjkfTg,2009
|
595
595
|
vellum/client/types/slim_workflow_execution_read.py,sha256=Opm1HTYVMz_D2USQCB-5ZoJ4EjKKfrDhoXc0hETldVM,1936
|
@@ -844,7 +844,7 @@ vellum/evaluations/utils/env.py,sha256=Xj_nxsoU5ox06EOTjRopR4lrigQI6Le6qbWGltYoE
|
|
844
844
|
vellum/evaluations/utils/exceptions.py,sha256=dXMAkzqbHV_AP5FjjbegPlfUE0zQDlpA3qOsoOJUxfg,49
|
845
845
|
vellum/evaluations/utils/paginator.py,sha256=rEED_BJAXAM6tM1yMwHePNzszjq_tTq4NbQvi1jWQ_Q,697
|
846
846
|
vellum/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
847
|
-
vellum/plugins/pydantic.py,sha256=
|
847
|
+
vellum/plugins/pydantic.py,sha256=SamPlRZ8V9kuxEfMkOPKjhMMLa5Q3wYJ3Z-F_IfKtvA,3911
|
848
848
|
vellum/plugins/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
849
849
|
vellum/plugins/tests/test_pydantic.py,sha256=S6bLqs3Y5DGA012QV_7f6hk4e6Bz-iJ9py9DEKuo4fM,746
|
850
850
|
vellum/plugins/utils.py,sha256=cPmxE9R2CK1bki2jKE8rB-G9zMf2pzHjSPDHFPXwd3Q,878
|
@@ -1597,7 +1597,8 @@ vellum/workflows/inputs/base.py,sha256=w3owT5B3rLBmIj-v-jL2l-HD4yd3hXK9RmHVd557B
|
|
1597
1597
|
vellum/workflows/inputs/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1598
1598
|
vellum/workflows/inputs/tests/test_inputs.py,sha256=lioA8917mFLYq7Ml69UNkqUjcWbbxkxnpIEJ4FBaYBk,2206
|
1599
1599
|
vellum/workflows/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1600
|
-
vellum/workflows/integrations/composio_service.py,sha256=
|
1600
|
+
vellum/workflows/integrations/composio_service.py,sha256=Wo1IKHr_CzrHFQHR_hjVcKap3JLwlV_TEbKsZcuR6Sw,5664
|
1601
|
+
vellum/workflows/integrations/mcp_service.py,sha256=SaOLg76JBAiBDAMUn04mxVWmf2Btobd1kDjc8B1atng,8712
|
1601
1602
|
vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
|
1602
1603
|
vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
|
1603
1604
|
vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
|
@@ -1695,13 +1696,13 @@ vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py,sha2
|
|
1695
1696
|
vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=VepO5z1277c1y5N6LLIC31nnWD1aak2m5oPFplfJHHs,6935
|
1696
1697
|
vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=dc3EEn1sOICpr3GdS8eyeFtExaGwWWcw9eHSdkRhQJU,2584
|
1697
1698
|
vellum/workflows/nodes/displayable/tool_calling_node/__init__.py,sha256=3n0-ysmFKsr40CVxPthc0rfJgqVJeZuUEsCmYudLVRg,117
|
1698
|
-
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=
|
1699
|
+
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=6uFt-_woayqyvbVGNJeWoiBQs2_VBtgTTdMX-Ztwx1I,7338
|
1699
1700
|
vellum/workflows/nodes/displayable/tool_calling_node/state.py,sha256=oQg_GAtc349nPB5BL_oeDYYD7q1qSDPAqjj8iA8OoAw,215
|
1700
1701
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1701
1702
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=UV0vZpU7-_tHcwnIq36WKwHrJXNurU4bdC3rfaw8eoU,4804
|
1702
|
-
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=
|
1703
|
-
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=
|
1704
|
-
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=
|
1703
|
+
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=U14GS6YwJGpIA13nq4Qvo1YBEku5ZRL627ZWnybp4hg,8029
|
1704
|
+
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=3TQeLD-HlB0OHEtUcADe4jl0XGi9YTv5FL4ZupDaSJA,8361
|
1705
|
+
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=shFC4Vm0vyqAILWsUTDOo_Ggn0_7GHKQlgZ0TPHohWQ,19180
|
1705
1706
|
vellum/workflows/nodes/experimental/README.md,sha256=eF6DfIL8t-HbF9-mcofOMymKrraiBHDLKTlnBa51ZiE,284
|
1706
1707
|
vellum/workflows/nodes/experimental/__init__.py,sha256=jCQgvZEknXKfuNhGSOou4XPfrPqZ1_XBj5F0n0fgiWM,106
|
1707
1708
|
vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
|
@@ -1748,7 +1749,7 @@ vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVi
|
|
1748
1749
|
vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
|
1749
1750
|
vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
|
1750
1751
|
vellum/workflows/types/code_execution_node_wrappers.py,sha256=3MNIoFZKzVzNS5qFLVuDwMV17QJw72zo7NRf52yMq5A,3074
|
1751
|
-
vellum/workflows/types/core.py,sha256=
|
1752
|
+
vellum/workflows/types/core.py,sha256=ybQj70QjlVNSEXsdIO8Mug9EBIDEkyFPxYv4cnn_hM0,1482
|
1752
1753
|
vellum/workflows/types/definition.py,sha256=pK0fAXHw7C0AFpCoM4WGe1_MD-usupF4-m6ldo5AQXY,4568
|
1753
1754
|
vellum/workflows/types/generics.py,sha256=8jptbEx1fnJV0Lhj0MpCJOT6yNiEWeTOYOwrEAb5CRU,1576
|
1754
1755
|
vellum/workflows/types/stack.py,sha256=h7NE0vXR7l9DevFBIzIAk1Zh59K-kECQtDTKOUunwMY,1314
|
@@ -1774,8 +1775,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1774
1775
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1775
1776
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=ptMntHzVyy8ZuzNgeTuk7hREgKQ5UBdgq8VJFSGaW4Y,20832
|
1776
1777
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1777
|
-
vellum_ai-1.0.
|
1778
|
-
vellum_ai-1.0.
|
1779
|
-
vellum_ai-1.0.
|
1780
|
-
vellum_ai-1.0.
|
1781
|
-
vellum_ai-1.0.
|
1778
|
+
vellum_ai-1.0.9.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1779
|
+
vellum_ai-1.0.9.dist-info/METADATA,sha256=b1ciqHMubElgtkn0rm_nW7vFBfZ2KqdOVuogZ9ICOcE,5554
|
1780
|
+
vellum_ai-1.0.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1781
|
+
vellum_ai-1.0.9.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1782
|
+
vellum_ai-1.0.9.dist-info/RECORD,,
|
vellum_cli/push.py
CHANGED
@@ -67,7 +67,10 @@ def push_command(
|
|
67
67
|
raise ValueError(f"No workflow config for '{module}' found in project to push.")
|
68
68
|
|
69
69
|
if len(workflow_configs) > 1:
|
70
|
-
raise ValueError(
|
70
|
+
raise ValueError(
|
71
|
+
"Multiple workflows found. Please specify a single workflow to push. "
|
72
|
+
f"Found: {', '.join([w.module for w in workflow_configs])}"
|
73
|
+
)
|
71
74
|
|
72
75
|
workflow_config = workflow_configs[0]
|
73
76
|
|
vellum_cli/tests/test_push.py
CHANGED
@@ -77,9 +77,8 @@ def test_push__multiple_workflows_configured__no_module_specified(mock_module):
|
|
77
77
|
# THEN it should fail
|
78
78
|
assert result.exit_code == 1
|
79
79
|
assert result.exception
|
80
|
-
assert (
|
81
|
-
|
82
|
-
== "Multiple workflows found in project to push. Pushing only a single workflow is supported."
|
80
|
+
assert str(result.exception) == (
|
81
|
+
"Multiple workflows found. Please specify a single workflow to push. Found: examples.mock, examples.mock2"
|
83
82
|
)
|
84
83
|
|
85
84
|
|
@@ -349,9 +348,8 @@ def test_push__workflow_sandbox_option__existing_id_different_module(mock_module
|
|
349
348
|
# THEN it should fail
|
350
349
|
assert result.exit_code == 1
|
351
350
|
assert result.exception
|
352
|
-
assert (
|
353
|
-
|
354
|
-
== "Multiple workflows found in project to push. Pushing only a single workflow is supported."
|
351
|
+
assert str(result.exception) == (
|
352
|
+
f"Multiple workflows found. Please specify a single workflow to push. Found: {module}, {second_module}"
|
355
353
|
)
|
356
354
|
|
357
355
|
|
@@ -407,6 +407,11 @@ def test_serialize_workflow():
|
|
407
407
|
],
|
408
408
|
},
|
409
409
|
},
|
410
|
+
{
|
411
|
+
"id": "89084be2-0853-4483-9031-c24bdf872c32",
|
412
|
+
"name": "tool_sources",
|
413
|
+
"value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": []}},
|
414
|
+
},
|
410
415
|
{
|
411
416
|
"id": "229cd1ca-dc2f-4586-b933-c4d4966f7bd1",
|
412
417
|
"name": "parameters",
|
@@ -175,6 +175,11 @@ def test_serialize_workflow():
|
|
175
175
|
],
|
176
176
|
},
|
177
177
|
},
|
178
|
+
{
|
179
|
+
"id": "89084be2-0853-4483-9031-c24bdf872c32",
|
180
|
+
"name": "tool_sources",
|
181
|
+
"value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": []}},
|
182
|
+
},
|
178
183
|
{
|
179
184
|
"id": "229cd1ca-dc2f-4586-b933-c4d4966f7bd1",
|
180
185
|
"name": "parameters",
|
File without changes
|
File without changes
|
File without changes
|