vellum-ai 1.0.9__py3-none-any.whl → 1.0.10__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/emitters/__init__.py +2 -0
- vellum/workflows/emitters/base.py +17 -0
- vellum/workflows/emitters/vellum_emitter.py +138 -0
- vellum/workflows/integrations/composio_service.py +6 -2
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +18 -19
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py +43 -0
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +22 -2
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py +7 -5
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +51 -38
- vellum/workflows/types/core.py +3 -5
- vellum/workflows/types/definition.py +1 -0
- vellum/workflows/types/tests/test_definition.py +4 -1
- {vellum_ai-1.0.9.dist-info → vellum_ai-1.0.10.dist-info}/METADATA +1 -1
- {vellum_ai-1.0.9.dist-info → vellum_ai-1.0.10.dist-info}/RECORD +21 -20
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +1 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +0 -5
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +0 -5
- {vellum_ai-1.0.9.dist-info → vellum_ai-1.0.10.dist-info}/LICENSE +0 -0
- {vellum_ai-1.0.9.dist-info → vellum_ai-1.0.10.dist-info}/WHEEL +0 -0
- {vellum_ai-1.0.9.dist-info → vellum_ai-1.0.10.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.10",
|
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.10",
|
32
32
|
}
|
33
33
|
if self._api_version is not None:
|
34
34
|
headers["X-API-Version"] = self._api_version
|
@@ -1,10 +1,27 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
2
3
|
|
3
4
|
from vellum.workflows.events.workflow import WorkflowEvent
|
4
5
|
from vellum.workflows.state.base import BaseState
|
5
6
|
|
7
|
+
# To protect against circular imports
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from vellum.workflows.state.context import WorkflowContext
|
10
|
+
|
6
11
|
|
7
12
|
class BaseWorkflowEmitter(ABC):
|
13
|
+
def __init__(self):
|
14
|
+
self._context: Optional["WorkflowContext"] = None
|
15
|
+
|
16
|
+
def register_context(self, context: "WorkflowContext") -> None:
|
17
|
+
"""
|
18
|
+
Register the workflow context with this emitter.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
context: The workflow context containing shared resources like vellum_client.
|
22
|
+
"""
|
23
|
+
self._context = context
|
24
|
+
|
8
25
|
@abstractmethod
|
9
26
|
def emit_event(self, event: WorkflowEvent) -> None:
|
10
27
|
pass
|
@@ -0,0 +1,138 @@
|
|
1
|
+
import logging
|
2
|
+
import time
|
3
|
+
from typing import Any, Dict, Optional
|
4
|
+
|
5
|
+
import httpx
|
6
|
+
|
7
|
+
from vellum.workflows.emitters.base import BaseWorkflowEmitter
|
8
|
+
from vellum.workflows.events.types import default_serializer
|
9
|
+
from vellum.workflows.events.workflow import WorkflowEvent
|
10
|
+
from vellum.workflows.state.base import BaseState
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
class VellumEmitter(BaseWorkflowEmitter):
|
16
|
+
"""
|
17
|
+
Emitter that sends workflow events to Vellum's infrastructure for monitoring
|
18
|
+
externally hosted SDK-powered workflows.
|
19
|
+
|
20
|
+
Usage:
|
21
|
+
class MyWorkflow(BaseWorkflow):
|
22
|
+
emitters = [VellumEmitter]
|
23
|
+
|
24
|
+
The emitter will automatically use the same Vellum client configuration
|
25
|
+
as the workflow it's attached to.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
*,
|
31
|
+
timeout: Optional[float] = 30.0,
|
32
|
+
max_retries: int = 3,
|
33
|
+
):
|
34
|
+
"""
|
35
|
+
Initialize the VellumEmitter.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
timeout: Request timeout in seconds.
|
39
|
+
max_retries: Maximum number of retry attempts for failed requests.
|
40
|
+
"""
|
41
|
+
super().__init__()
|
42
|
+
self._timeout = timeout
|
43
|
+
self._max_retries = max_retries
|
44
|
+
self._events_endpoint = "events" # TODO: make this configurable with the correct url
|
45
|
+
|
46
|
+
def emit_event(self, event: WorkflowEvent) -> None:
|
47
|
+
"""
|
48
|
+
Emit a workflow event to Vellum's infrastructure.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
event: The workflow event to emit.
|
52
|
+
"""
|
53
|
+
if not self._context:
|
54
|
+
return
|
55
|
+
|
56
|
+
try:
|
57
|
+
event_data = default_serializer(event)
|
58
|
+
|
59
|
+
self._send_event(event_data)
|
60
|
+
|
61
|
+
except Exception as e:
|
62
|
+
logger.exception(f"Failed to emit event {event.name}: {e}")
|
63
|
+
|
64
|
+
def snapshot_state(self, state: BaseState) -> None:
|
65
|
+
"""
|
66
|
+
Send a state snapshot to Vellum's infrastructure.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
state: The workflow state to snapshot.
|
70
|
+
"""
|
71
|
+
pass
|
72
|
+
|
73
|
+
def _send_event(self, event_data: Dict[str, Any]) -> None:
|
74
|
+
"""
|
75
|
+
Send event data to Vellum's events endpoint with retry logic.
|
76
|
+
|
77
|
+
Args:
|
78
|
+
event_data: The serialized event data to send.
|
79
|
+
"""
|
80
|
+
if not self._context:
|
81
|
+
logger.warning("Cannot send event: No workflow context registered")
|
82
|
+
return
|
83
|
+
|
84
|
+
client = self._context.vellum_client
|
85
|
+
|
86
|
+
for attempt in range(self._max_retries + 1):
|
87
|
+
try:
|
88
|
+
# Use the Vellum client's underlying HTTP client to make the request
|
89
|
+
# For proper authentication headers and configuration
|
90
|
+
base_url = client._client_wrapper.get_environment().default
|
91
|
+
response = client._client_wrapper.httpx_client.request(
|
92
|
+
method="POST",
|
93
|
+
path=f"{base_url}/{self._events_endpoint}", # TODO: will be replaced with the correct url
|
94
|
+
json=event_data,
|
95
|
+
headers=client._client_wrapper.get_headers(),
|
96
|
+
request_options={"timeout_in_seconds": self._timeout},
|
97
|
+
)
|
98
|
+
|
99
|
+
response.raise_for_status()
|
100
|
+
|
101
|
+
if attempt > 0:
|
102
|
+
logger.info(f"Event sent successfully after {attempt + 1} attempts")
|
103
|
+
return
|
104
|
+
|
105
|
+
except httpx.HTTPStatusError as e:
|
106
|
+
if e.response.status_code >= 500:
|
107
|
+
# Server errors might be transient, retry
|
108
|
+
if attempt < self._max_retries:
|
109
|
+
wait_time = min(2**attempt, 60) # Exponential backoff, max 60s
|
110
|
+
logger.warning(
|
111
|
+
f"Server error emitting event (attempt {attempt + 1}/{self._max_retries + 1}): "
|
112
|
+
f"{e.response.status_code}. Retrying in {wait_time}s..."
|
113
|
+
)
|
114
|
+
time.sleep(wait_time)
|
115
|
+
continue
|
116
|
+
else:
|
117
|
+
logger.exception(
|
118
|
+
f"Server error emitting event after {self._max_retries + 1} attempts: "
|
119
|
+
f"{e.response.status_code} {e.response.text}"
|
120
|
+
)
|
121
|
+
return
|
122
|
+
else:
|
123
|
+
# Client errors (4xx) are not retriable
|
124
|
+
logger.exception(f"Client error emitting event: {e.response.status_code} {e.response.text}")
|
125
|
+
return
|
126
|
+
|
127
|
+
except httpx.RequestError as e:
|
128
|
+
if attempt < self._max_retries:
|
129
|
+
wait_time = min(2**attempt, 60) # Exponential backoff, max 60s
|
130
|
+
logger.warning(
|
131
|
+
f"Network error emitting event (attempt {attempt + 1}/{self._max_retries + 1}): "
|
132
|
+
f"{e}. Retrying in {wait_time}s..."
|
133
|
+
)
|
134
|
+
time.sleep(wait_time)
|
135
|
+
continue
|
136
|
+
else:
|
137
|
+
logger.exception(f"Network error emitting event after {self._max_retries + 1} attempts: {e}")
|
138
|
+
return
|
@@ -135,16 +135,20 @@ class ComposioService:
|
|
135
135
|
else:
|
136
136
|
raise NodeException(f"Failed to retrieve tool details for '{tool_slug}': {error_message}")
|
137
137
|
|
138
|
-
def execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
|
138
|
+
def execute_tool(self, tool_name: str, arguments: Dict[str, Any], user_id: Optional[str] = None) -> Any:
|
139
139
|
"""Execute a tool using direct API request
|
140
140
|
|
141
141
|
Args:
|
142
142
|
tool_name: The name of the tool to execute (e.g., "HACKERNEWS_GET_USER")
|
143
143
|
arguments: Dictionary of arguments to pass to the tool
|
144
|
+
user_id: Optional user ID to identify which user's Composio connection to use
|
144
145
|
|
145
146
|
Returns:
|
146
147
|
The result of the tool execution
|
147
148
|
"""
|
148
149
|
endpoint = f"/tools/execute/{tool_name}"
|
149
|
-
|
150
|
+
json_data: Dict[str, Any] = {"arguments": arguments}
|
151
|
+
if user_id is not None:
|
152
|
+
json_data["user_id"] = user_id
|
153
|
+
response = self._make_request(endpoint, method="POST", json_data=json_data)
|
150
154
|
return response.get("data", response)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, ClassVar, Dict, Iterator, List, Optional, Set, Union
|
1
|
+
from typing import Any, ClassVar, Dict, Generic, Iterator, List, Optional, Set, Union
|
2
2
|
|
3
3
|
from vellum import ChatMessage, PromptBlock
|
4
4
|
from vellum.client.types.prompt_parameters import PromptParameters
|
@@ -13,18 +13,21 @@ from vellum.workflows.nodes.bases import BaseNode
|
|
13
13
|
from vellum.workflows.nodes.displayable.tool_calling_node.state import ToolCallingState
|
14
14
|
from vellum.workflows.nodes.displayable.tool_calling_node.utils import (
|
15
15
|
create_function_node,
|
16
|
+
create_mcp_tool_node,
|
16
17
|
create_tool_router_node,
|
17
18
|
get_function_name,
|
19
|
+
get_mcp_tool_name,
|
18
20
|
hydrate_mcp_tool_definitions,
|
19
21
|
)
|
20
22
|
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
21
23
|
from vellum.workflows.state.context import WorkflowContext
|
22
|
-
from vellum.workflows.types.core import EntityInputsInterface, Tool
|
24
|
+
from vellum.workflows.types.core import EntityInputsInterface, Tool
|
23
25
|
from vellum.workflows.types.definition import MCPServer
|
26
|
+
from vellum.workflows.types.generics import StateType
|
24
27
|
from vellum.workflows.workflows.event_filters import all_workflow_event_filter
|
25
28
|
|
26
29
|
|
27
|
-
class ToolCallingNode(BaseNode):
|
30
|
+
class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
28
31
|
"""
|
29
32
|
A Node that dynamically invokes the provided functions to the underlying Prompt
|
30
33
|
|
@@ -32,7 +35,6 @@ class ToolCallingNode(BaseNode):
|
|
32
35
|
ml_model: str - The model to use for tool calling (e.g., "gpt-4o-mini")
|
33
36
|
blocks: List[PromptBlock] - The prompt blocks to use (same format as InlinePromptNode)
|
34
37
|
functions: List[Tool] - The functions that can be called
|
35
|
-
tool_sources: List[ToolSource] - The tool sources that can be called
|
36
38
|
prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values
|
37
39
|
parameters: PromptParameters - The parameters for the Prompt
|
38
40
|
max_prompt_iterations: Optional[int] - Maximum number of prompt iterations before stopping
|
@@ -41,7 +43,6 @@ class ToolCallingNode(BaseNode):
|
|
41
43
|
ml_model: ClassVar[str] = "gpt-4o-mini"
|
42
44
|
blocks: ClassVar[List[Union[PromptBlock, Dict[str, Any]]]] = []
|
43
45
|
functions: ClassVar[List[Tool]] = []
|
44
|
-
tool_sources: ClassVar[List[ToolSource]] = []
|
45
46
|
prompt_inputs: ClassVar[Optional[EntityInputsInterface]] = None
|
46
47
|
parameters: PromptParameters = DEFAULT_PROMPT_PARAMETERS
|
47
48
|
max_prompt_iterations: ClassVar[Optional[int]] = 5
|
@@ -138,7 +139,6 @@ class ToolCallingNode(BaseNode):
|
|
138
139
|
ml_model=self.ml_model,
|
139
140
|
blocks=self.blocks,
|
140
141
|
functions=self.functions,
|
141
|
-
tool_sources=self.tool_sources,
|
142
142
|
prompt_inputs=self.prompt_inputs,
|
143
143
|
parameters=self.parameters,
|
144
144
|
max_prompt_iterations=self.max_prompt_iterations,
|
@@ -146,23 +146,22 @@ class ToolCallingNode(BaseNode):
|
|
146
146
|
|
147
147
|
self._function_nodes = {}
|
148
148
|
for function in self.functions:
|
149
|
-
|
150
|
-
|
151
|
-
self._function_nodes[function_name] = create_function_node(
|
152
|
-
function=function,
|
153
|
-
tool_router_node=self.tool_router_node,
|
154
|
-
)
|
155
|
-
|
156
|
-
for tool_source in self.tool_sources:
|
157
|
-
if isinstance(tool_source, MCPServer):
|
158
|
-
tool_definitions = hydrate_mcp_tool_definitions(tool_source)
|
149
|
+
if isinstance(function, MCPServer):
|
150
|
+
tool_definitions = hydrate_mcp_tool_definitions(function)
|
159
151
|
for tool_definition in tool_definitions:
|
160
|
-
function_name =
|
152
|
+
function_name = get_mcp_tool_name(tool_definition)
|
161
153
|
|
162
|
-
self._function_nodes[function_name] =
|
163
|
-
|
154
|
+
self._function_nodes[function_name] = create_mcp_tool_node(
|
155
|
+
tool_def=tool_definition,
|
164
156
|
tool_router_node=self.tool_router_node,
|
165
157
|
)
|
158
|
+
else:
|
159
|
+
function_name = get_function_name(function)
|
160
|
+
|
161
|
+
self._function_nodes[function_name] = create_function_node(
|
162
|
+
function=function,
|
163
|
+
tool_router_node=self.tool_router_node,
|
164
|
+
)
|
166
165
|
|
167
166
|
graph_set = set()
|
168
167
|
|
@@ -125,3 +125,46 @@ class TestComposioCoreService:
|
|
125
125
|
timeout=30,
|
126
126
|
)
|
127
127
|
assert result == {"items": [], "total": 0}
|
128
|
+
|
129
|
+
def test_execute_tool_with_user_id(self, composio_service, mock_requests, mock_tool_execution_response):
|
130
|
+
"""Test executing a tool with user_id parameter"""
|
131
|
+
# GIVEN a user_id and tool arguments
|
132
|
+
user_id = "test_user_123"
|
133
|
+
tool_args = {"param1": "value1"}
|
134
|
+
mock_response = Mock()
|
135
|
+
mock_response.json.return_value = mock_tool_execution_response
|
136
|
+
mock_response.raise_for_status.return_value = None
|
137
|
+
mock_requests.post.return_value = mock_response
|
138
|
+
|
139
|
+
# WHEN we execute a tool with user_id
|
140
|
+
result = composio_service.execute_tool("TEST_TOOL", tool_args, user_id=user_id)
|
141
|
+
|
142
|
+
# THEN the user_id should be included in the request payload
|
143
|
+
mock_requests.post.assert_called_once_with(
|
144
|
+
"https://backend.composio.dev/api/v3/tools/execute/TEST_TOOL",
|
145
|
+
headers={"x-api-key": "test-key", "Content-Type": "application/json"},
|
146
|
+
json={"arguments": tool_args, "user_id": user_id},
|
147
|
+
timeout=30,
|
148
|
+
)
|
149
|
+
assert result == {"items": [], "total": 0}
|
150
|
+
|
151
|
+
def test_execute_tool_without_user_id(self, composio_service, mock_requests, mock_tool_execution_response):
|
152
|
+
"""Test executing a tool without user_id parameter maintains backward compatibility"""
|
153
|
+
# GIVEN tool arguments without user_id
|
154
|
+
tool_args = {"param1": "value1"}
|
155
|
+
mock_response = Mock()
|
156
|
+
mock_response.json.return_value = mock_tool_execution_response
|
157
|
+
mock_response.raise_for_status.return_value = None
|
158
|
+
mock_requests.post.return_value = mock_response
|
159
|
+
|
160
|
+
# WHEN we execute a tool without user_id
|
161
|
+
result = composio_service.execute_tool("TEST_TOOL", tool_args)
|
162
|
+
|
163
|
+
# THEN the user_id should NOT be included in the request payload
|
164
|
+
mock_requests.post.assert_called_once_with(
|
165
|
+
"https://backend.composio.dev/api/v3/tools/execute/TEST_TOOL",
|
166
|
+
headers={"x-api-key": "test-key", "Content-Type": "application/json"},
|
167
|
+
json={"arguments": tool_args},
|
168
|
+
timeout=30,
|
169
|
+
)
|
170
|
+
assert result == {"items": [], "total": 0}
|
@@ -39,7 +39,6 @@ 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=[],
|
43
42
|
prompt_inputs=None,
|
44
43
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
45
44
|
)
|
@@ -98,7 +97,6 @@ def test_tool_calling_node_inline_workflow_context():
|
|
98
97
|
ml_model="test-model",
|
99
98
|
blocks=[],
|
100
99
|
functions=[MyWorkflow],
|
101
|
-
tool_sources=[],
|
102
100
|
prompt_inputs=None,
|
103
101
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
104
102
|
)
|
@@ -213,3 +211,25 @@ def test_tool_calling_node_with_user_provided_chat_history_block(vellum_adhoc_pr
|
|
213
211
|
]
|
214
212
|
assert len(chat_history_inputs) == 1
|
215
213
|
assert chat_history_inputs[0].value == [ChatMessage(role="USER", text="Hello from user")]
|
214
|
+
|
215
|
+
|
216
|
+
def test_tool_calling_node_with_generic_type_parameter():
|
217
|
+
# GIVEN a custom state class
|
218
|
+
class State(BaseState):
|
219
|
+
pass
|
220
|
+
|
221
|
+
# AND a ToolCallingNode that uses the generic type parameter
|
222
|
+
class TestToolCallingNode(ToolCallingNode[State]):
|
223
|
+
ml_model = "gpt-4o-mini"
|
224
|
+
blocks = []
|
225
|
+
functions = [first_function]
|
226
|
+
max_prompt_iterations = 1
|
227
|
+
|
228
|
+
# WHEN we create an instance of the node
|
229
|
+
state = State()
|
230
|
+
node = TestToolCallingNode(state=state)
|
231
|
+
|
232
|
+
# THEN the node should be created successfully
|
233
|
+
assert node is not None
|
234
|
+
assert isinstance(node, TestToolCallingNode)
|
235
|
+
assert node.state == state
|
@@ -12,7 +12,11 @@ from vellum.prompts.constants import DEFAULT_PROMPT_PARAMETERS
|
|
12
12
|
from vellum.workflows import BaseWorkflow
|
13
13
|
from vellum.workflows.inputs.base import BaseInputs
|
14
14
|
from vellum.workflows.nodes.bases import BaseNode
|
15
|
-
from vellum.workflows.nodes.displayable.tool_calling_node.utils import
|
15
|
+
from vellum.workflows.nodes.displayable.tool_calling_node.utils import (
|
16
|
+
create_tool_router_node,
|
17
|
+
get_function_name,
|
18
|
+
get_mcp_tool_name,
|
19
|
+
)
|
16
20
|
from vellum.workflows.outputs.base import BaseOutputs
|
17
21
|
from vellum.workflows.state.base import BaseState
|
18
22
|
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPServer, MCPToolDefinition
|
@@ -77,7 +81,7 @@ def test_get_function_name_mcp_tool_definition():
|
|
77
81
|
parameters={"repository_name": "string", "description": "string"},
|
78
82
|
)
|
79
83
|
|
80
|
-
result =
|
84
|
+
result = get_mcp_tool_name(mcp_tool)
|
81
85
|
|
82
86
|
assert result == "github__create_repository"
|
83
87
|
|
@@ -93,7 +97,7 @@ def test_get_function_name_composio_tool_definition_various_toolkits(
|
|
93
97
|
toolkit: str, action: str, description: str, expected_result: str
|
94
98
|
):
|
95
99
|
"""Test ComposioToolDefinition function name generation with various toolkits."""
|
96
|
-
composio_tool = ComposioToolDefinition(toolkit=toolkit, action=action, description=description)
|
100
|
+
composio_tool = ComposioToolDefinition(toolkit=toolkit, action=action, description=description, user_id=None)
|
97
101
|
|
98
102
|
result = get_function_name(composio_tool)
|
99
103
|
|
@@ -106,7 +110,6 @@ def test_create_tool_router_node_max_prompt_iterations(vellum_adhoc_prompt_clien
|
|
106
110
|
ml_model="gpt-4o-mini",
|
107
111
|
blocks=[],
|
108
112
|
functions=[],
|
109
|
-
tool_sources=[],
|
110
113
|
prompt_inputs=None,
|
111
114
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
112
115
|
max_prompt_iterations=None,
|
@@ -166,7 +169,6 @@ def test_create_tool_router_node_chat_history_block_dict(vellum_adhoc_prompt_cli
|
|
166
169
|
ml_model="gpt-4o-mini",
|
167
170
|
blocks=blocks, # type: ignore
|
168
171
|
functions=[],
|
169
|
-
tool_sources=[],
|
170
172
|
prompt_inputs=None,
|
171
173
|
parameters=DEFAULT_PROMPT_PARAMETERS,
|
172
174
|
)
|
@@ -28,7 +28,7 @@ from vellum.workflows.ports.port import Port
|
|
28
28
|
from vellum.workflows.references.lazy import LazyReference
|
29
29
|
from vellum.workflows.state import BaseState
|
30
30
|
from vellum.workflows.state.encoder import DefaultStateEncoder
|
31
|
-
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior, Tool,
|
31
|
+
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior, Tool, ToolBase
|
32
32
|
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPServer, MCPToolDefinition
|
33
33
|
from vellum.workflows.types.generics import is_workflow_class
|
34
34
|
|
@@ -184,7 +184,12 @@ class ComposioNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
184
184
|
try:
|
185
185
|
# Execute using ComposioService
|
186
186
|
composio_service = ComposioService()
|
187
|
-
|
187
|
+
if self.composio_tool.user_id is not None:
|
188
|
+
result = composio_service.execute_tool(
|
189
|
+
tool_name=self.composio_tool.action, arguments=arguments, user_id=self.composio_tool.user_id
|
190
|
+
)
|
191
|
+
else:
|
192
|
+
result = composio_service.execute_tool(tool_name=self.composio_tool.action, arguments=arguments)
|
188
193
|
except Exception as e:
|
189
194
|
raise NodeException(
|
190
195
|
message=f"Error executing Composio tool '{self.composio_tool.action}': {str(e)}",
|
@@ -249,6 +254,7 @@ def _hydrate_composio_tool_definition(tool_def: ComposioToolDefinition) -> Compo
|
|
249
254
|
parameters=tool_details.get("input_parameters", tool_def.parameters),
|
250
255
|
version=tool_details.get("version", tool_def.version),
|
251
256
|
tags=tool_details.get("tags", tool_def.tags),
|
257
|
+
user_id=tool_def.user_id,
|
252
258
|
)
|
253
259
|
|
254
260
|
except Exception as e:
|
@@ -281,12 +287,11 @@ def create_tool_router_node(
|
|
281
287
|
ml_model: str,
|
282
288
|
blocks: List[Union[PromptBlock, Dict[str, Any]]],
|
283
289
|
functions: List[Tool],
|
284
|
-
tool_sources: List[ToolSource],
|
285
290
|
prompt_inputs: Optional[EntityInputsInterface],
|
286
291
|
parameters: PromptParameters,
|
287
292
|
max_prompt_iterations: Optional[int] = None,
|
288
293
|
) -> Type[ToolRouterNode]:
|
289
|
-
if functions and len(functions) > 0
|
294
|
+
if functions and len(functions) > 0:
|
290
295
|
# Create dynamic ports and convert functions in a single loop
|
291
296
|
Ports = type("Ports", (), {})
|
292
297
|
prompt_functions: List[Union[Tool, FunctionDefinition]] = []
|
@@ -315,27 +320,28 @@ def create_tool_router_node(
|
|
315
320
|
parameters=enhanced_function.parameters,
|
316
321
|
)
|
317
322
|
)
|
323
|
+
# Create port for this function (using original function for get_function_name)
|
324
|
+
function_name = get_function_name(function)
|
325
|
+
port = create_port_condition(function_name)
|
326
|
+
setattr(Ports, function_name, port)
|
327
|
+
elif isinstance(function, MCPServer):
|
328
|
+
tool_functions: List[MCPToolDefinition] = hydrate_mcp_tool_definitions(function)
|
329
|
+
for tool_function in tool_functions:
|
330
|
+
name = get_mcp_tool_name(tool_function)
|
331
|
+
prompt_functions.append(
|
332
|
+
FunctionDefinition(
|
333
|
+
name=name,
|
334
|
+
description=tool_function.description,
|
335
|
+
parameters=tool_function.parameters,
|
336
|
+
)
|
337
|
+
)
|
338
|
+
port = create_port_condition(name)
|
339
|
+
setattr(Ports, name, port)
|
318
340
|
else:
|
319
341
|
prompt_functions.append(function)
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
port = create_port_condition(function_name)
|
324
|
-
setattr(Ports, function_name, port)
|
325
|
-
|
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,
|
335
|
-
)
|
336
|
-
)
|
337
|
-
port = create_port_condition(name)
|
338
|
-
setattr(Ports, name, port)
|
342
|
+
function_name = get_function_name(function)
|
343
|
+
port = create_port_condition(function_name)
|
344
|
+
setattr(Ports, function_name, port)
|
339
345
|
|
340
346
|
# Add the else port for when no function conditions match
|
341
347
|
setattr(Ports, "default", Port.on_else())
|
@@ -397,7 +403,7 @@ def create_tool_router_node(
|
|
397
403
|
|
398
404
|
|
399
405
|
def create_function_node(
|
400
|
-
function:
|
406
|
+
function: ToolBase,
|
401
407
|
tool_router_node: Type[ToolRouterNode],
|
402
408
|
) -> Type[BaseNode]:
|
403
409
|
"""
|
@@ -438,17 +444,6 @@ def create_function_node(
|
|
438
444
|
},
|
439
445
|
)
|
440
446
|
return node
|
441
|
-
elif isinstance(function, MCPToolDefinition):
|
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
|
452
447
|
elif is_workflow_class(function):
|
453
448
|
node = type(
|
454
449
|
f"DynamicInlineSubworkflowNode_{function.__name__}",
|
@@ -474,13 +469,31 @@ def create_function_node(
|
|
474
469
|
return node
|
475
470
|
|
476
471
|
|
477
|
-
def
|
472
|
+
def create_mcp_tool_node(
|
473
|
+
tool_def: MCPToolDefinition,
|
474
|
+
tool_router_node: Type[ToolRouterNode],
|
475
|
+
) -> Type[BaseNode]:
|
476
|
+
node = type(
|
477
|
+
f"MCPNode_{tool_def.name}",
|
478
|
+
(MCPNode,),
|
479
|
+
{
|
480
|
+
"mcp_tool": tool_def,
|
481
|
+
"function_call_output": tool_router_node.Outputs.results,
|
482
|
+
"__module__": __name__,
|
483
|
+
},
|
484
|
+
)
|
485
|
+
return node
|
486
|
+
|
487
|
+
|
488
|
+
def get_function_name(function: ToolBase) -> str:
|
478
489
|
if isinstance(function, DeploymentDefinition):
|
479
490
|
name = str(function.deployment_id or function.deployment_name)
|
480
491
|
return name.replace("-", "")
|
481
492
|
elif isinstance(function, ComposioToolDefinition):
|
482
493
|
return function.name
|
483
|
-
elif isinstance(function, MCPToolDefinition):
|
484
|
-
return f"{function.server.name}__{function.name}"
|
485
494
|
else:
|
486
495
|
return snake_case(function.__name__)
|
496
|
+
|
497
|
+
|
498
|
+
def get_mcp_tool_name(tool_def: MCPToolDefinition) -> str:
|
499
|
+
return f"{tool_def.server.name}__{tool_def.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, MCPServer
|
16
|
+
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPServer
|
17
17
|
|
18
18
|
if TYPE_CHECKING:
|
19
19
|
from vellum.workflows.workflows.base import BaseWorkflow
|
@@ -51,7 +51,5 @@ class ConditionType(Enum):
|
|
51
51
|
|
52
52
|
|
53
53
|
# Type alias for functions that can be called in tool calling nodes
|
54
|
-
|
55
|
-
|
56
|
-
# Type alias for sources that provide tools to tool calling nodes
|
57
|
-
ToolSource = Union[MCPServer]
|
54
|
+
ToolBase = Union[Callable[..., Any], DeploymentDefinition, Type["BaseWorkflow"], ComposioToolDefinition]
|
55
|
+
Tool = Union[ToolBase, MCPServer]
|
@@ -39,7 +39,10 @@ def test_deployment_definition(deployment_value, expected_deployment_id, expecte
|
|
39
39
|
def test_composio_tool_definition_creation():
|
40
40
|
"""Test that ComposioToolDefinition can be created with required fields."""
|
41
41
|
composio_tool = ComposioToolDefinition(
|
42
|
-
toolkit="GITHUB",
|
42
|
+
toolkit="GITHUB",
|
43
|
+
action="GITHUB_CREATE_AN_ISSUE",
|
44
|
+
description="Create a new issue in a GitHub repository",
|
45
|
+
user_id=None,
|
43
46
|
)
|
44
47
|
|
45
48
|
assert composio_tool.toolkit == "GITHUB"
|
@@ -94,9 +94,9 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_
|
|
94
94
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=XWrhHg_acLsRHwjstBAii9Pmes9oXFtAUWSAVF1oSBc,11225
|
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
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=
|
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=
|
97
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=sWKSqw1B4iAEamIzbRJBfjtMCy_D54gxEu3aPSDrS_o,3819
|
98
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=4t1lkN2nsZF6lFqP6QnskUQWJlhasF8C2_f6atzk8ZY,26298
|
99
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=B0rDsCvO24qPp0gkmj8SdTDY5CxZYkvKwknsKBuAPyA,10017
|
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=PYKT4GSWVfnUCdamESx8hYo2KF9SVuaYQAHwwHxyYSI,2385
|
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
|
@@ -1536,8 +1536,9 @@ vellum/workflows/descriptors/tests/test_utils.py,sha256=HJ5DoRz0sJvViGxyZ_FtytZj
|
|
1536
1536
|
vellum/workflows/descriptors/utils.py,sha256=1siECBf6AI54gwwUwkF6mP9rYsRryUGaOYBbMpQaceM,3848
|
1537
1537
|
vellum/workflows/edges/__init__.py,sha256=wSkmAnz9xyi4vZwtDbKxwlplt2skD7n3NsxkvR_pUus,50
|
1538
1538
|
vellum/workflows/edges/edge.py,sha256=N0SnY3gKVuxImPAdCbPMPlHJIXbkQ3fwq_LbJRvVMFc,677
|
1539
|
-
vellum/workflows/emitters/__init__.py,sha256=
|
1540
|
-
vellum/workflows/emitters/base.py,sha256=
|
1539
|
+
vellum/workflows/emitters/__init__.py,sha256=d9QFOI3eVg6rzpSFLvrjkDYXWikf1tcp3ruTRa2Boyc,143
|
1540
|
+
vellum/workflows/emitters/base.py,sha256=Tcp13VMB-GMwEJdl-6XTPckspdOdwpMgBx22-PcQxds,892
|
1541
|
+
vellum/workflows/emitters/vellum_emitter.py,sha256=VRJgyEs6RnikwlPBUu1s7BD8flVeuM3QgTeQLUnaDuE,5051
|
1541
1542
|
vellum/workflows/environment/__init__.py,sha256=TJz0m9dwIs6YOwCTeuN0HHsU-ecyjc1OJXx4AFy83EQ,121
|
1542
1543
|
vellum/workflows/environment/environment.py,sha256=Ck3RPKXJvtMGx_toqYQQQF-ZwXm5ijVwJpEPTeIJ4_Q,471
|
1543
1544
|
vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
|
@@ -1597,7 +1598,7 @@ vellum/workflows/inputs/base.py,sha256=w3owT5B3rLBmIj-v-jL2l-HD4yd3hXK9RmHVd557B
|
|
1597
1598
|
vellum/workflows/inputs/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1598
1599
|
vellum/workflows/inputs/tests/test_inputs.py,sha256=lioA8917mFLYq7Ml69UNkqUjcWbbxkxnpIEJ4FBaYBk,2206
|
1599
1600
|
vellum/workflows/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1600
|
-
vellum/workflows/integrations/composio_service.py,sha256=
|
1601
|
+
vellum/workflows/integrations/composio_service.py,sha256=v1rVQXTh1rnupguj8oIM20V7bSKaJiAoJ5yjz2NeKA8,5906
|
1601
1602
|
vellum/workflows/integrations/mcp_service.py,sha256=SaOLg76JBAiBDAMUn04mxVWmf2Btobd1kDjc8B1atng,8712
|
1602
1603
|
vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
|
1603
1604
|
vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
|
@@ -1696,13 +1697,13 @@ vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py,sha2
|
|
1696
1697
|
vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=VepO5z1277c1y5N6LLIC31nnWD1aak2m5oPFplfJHHs,6935
|
1697
1698
|
vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=dc3EEn1sOICpr3GdS8eyeFtExaGwWWcw9eHSdkRhQJU,2584
|
1698
1699
|
vellum/workflows/nodes/displayable/tool_calling_node/__init__.py,sha256=3n0-ysmFKsr40CVxPthc0rfJgqVJeZuUEsCmYudLVRg,117
|
1699
|
-
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=
|
1700
|
+
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=ftPf7hmPvk_rJeIoxnJGkTLex-kDW1CRuvVDUwUdMxg,7283
|
1700
1701
|
vellum/workflows/nodes/displayable/tool_calling_node/state.py,sha256=oQg_GAtc349nPB5BL_oeDYYD7q1qSDPAqjj8iA8OoAw,215
|
1701
1702
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1702
|
-
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=
|
1703
|
-
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=
|
1704
|
-
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=
|
1705
|
-
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=
|
1703
|
+
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=y7KAqbiJHoya6N5EWv1qgz0htM_Yzz7zjAHVp78IMFo,6919
|
1704
|
+
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=TPafJhCAV6oLg5kTttQw0hL56ct3a2Xatvnld6dK8CY,8628
|
1705
|
+
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=om4FztVQ33jFZK_lbusi6khOM7zgzNCHlUcEb5-r6pU,8361
|
1706
|
+
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=fvy0O3YpibWUpw4aLKnk8PdwlRCJC7Z2acjryOTiuxY,19728
|
1706
1707
|
vellum/workflows/nodes/experimental/README.md,sha256=eF6DfIL8t-HbF9-mcofOMymKrraiBHDLKTlnBa51ZiE,284
|
1707
1708
|
vellum/workflows/nodes/experimental/__init__.py,sha256=jCQgvZEknXKfuNhGSOou4XPfrPqZ1_XBj5F0n0fgiWM,106
|
1708
1709
|
vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
|
@@ -1749,12 +1750,12 @@ vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVi
|
|
1749
1750
|
vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
|
1750
1751
|
vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
|
1751
1752
|
vellum/workflows/types/code_execution_node_wrappers.py,sha256=3MNIoFZKzVzNS5qFLVuDwMV17QJw72zo7NRf52yMq5A,3074
|
1752
|
-
vellum/workflows/types/core.py,sha256=
|
1753
|
-
vellum/workflows/types/definition.py,sha256=
|
1753
|
+
vellum/workflows/types/core.py,sha256=TggDVs2lVya33xvu374EDhMC1b7RRlAAs0zWLaF46BA,1385
|
1754
|
+
vellum/workflows/types/definition.py,sha256=fzWfsfbXLS4sZvjOQMSDoiuSaFo4Ii2kC8AOiPade9o,4602
|
1754
1755
|
vellum/workflows/types/generics.py,sha256=8jptbEx1fnJV0Lhj0MpCJOT6yNiEWeTOYOwrEAb5CRU,1576
|
1755
1756
|
vellum/workflows/types/stack.py,sha256=h7NE0vXR7l9DevFBIzIAk1Zh59K-kECQtDTKOUunwMY,1314
|
1756
1757
|
vellum/workflows/types/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1757
|
-
vellum/workflows/types/tests/test_definition.py,sha256=
|
1758
|
+
vellum/workflows/types/tests/test_definition.py,sha256=4Qqlf7GpoG9MrLuMCkcRzEZMgwrr7du4DROcB1xfv0E,5050
|
1758
1759
|
vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
|
1759
1760
|
vellum/workflows/types/utils.py,sha256=mTctHITBybpt4855x32oCKALBEcMNLn-9cCmfEKgJHQ,6498
|
1760
1761
|
vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1775,8 +1776,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1775
1776
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1776
1777
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=ptMntHzVyy8ZuzNgeTuk7hREgKQ5UBdgq8VJFSGaW4Y,20832
|
1777
1778
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1778
|
-
vellum_ai-1.0.
|
1779
|
-
vellum_ai-1.0.
|
1780
|
-
vellum_ai-1.0.
|
1781
|
-
vellum_ai-1.0.
|
1782
|
-
vellum_ai-1.0.
|
1779
|
+
vellum_ai-1.0.10.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1780
|
+
vellum_ai-1.0.10.dist-info/METADATA,sha256=j-b34bQSbPMhl5YG7dLuwC4_EZYCtRrdhoTxjXYJ3-U,5555
|
1781
|
+
vellum_ai-1.0.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1782
|
+
vellum_ai-1.0.10.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1783
|
+
vellum_ai-1.0.10.dist-info/RECORD,,
|
@@ -407,11 +407,6 @@ 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
|
-
},
|
415
410
|
{
|
416
411
|
"id": "229cd1ca-dc2f-4586-b933-c4d4966f7bd1",
|
417
412
|
"name": "parameters",
|
@@ -175,11 +175,6 @@ 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
|
-
},
|
183
178
|
{
|
184
179
|
"id": "229cd1ca-dc2f-4586-b933-c4d4966f7bd1",
|
185
180
|
"name": "parameters",
|
File without changes
|
File without changes
|
File without changes
|