vellum-ai 1.0.10__py3-none-any.whl → 1.1.0__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/__init__.py +2 -2
- vellum/client/__init__.py +0 -4
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/reference.md +2 -3
- vellum/client/resources/__init__.py +0 -2
- vellum/client/resources/workflow_deployments/client.py +119 -0
- vellum/client/types/__init__.py +2 -0
- vellum/client/types/api_request_parent_context.py +1 -0
- vellum/client/types/external_parent_context.py +36 -0
- vellum/client/types/node_execution_fulfilled_event.py +1 -0
- vellum/client/types/node_execution_initiated_event.py +1 -0
- vellum/client/types/node_execution_paused_event.py +1 -0
- vellum/client/types/node_execution_rejected_event.py +1 -0
- vellum/client/types/node_execution_resumed_event.py +1 -0
- vellum/client/types/node_execution_span.py +1 -0
- vellum/client/types/node_execution_span_attributes.py +1 -0
- vellum/client/types/node_execution_streaming_event.py +1 -0
- vellum/client/types/node_parent_context.py +1 -0
- vellum/client/types/parent_context.py +2 -0
- vellum/client/types/prompt_deployment_parent_context.py +1 -0
- vellum/client/types/slim_workflow_execution_read.py +1 -0
- vellum/client/types/span_link.py +1 -0
- vellum/client/types/workflow_deployment_event_executions_response.py +1 -0
- vellum/client/types/workflow_deployment_parent_context.py +1 -0
- vellum/client/types/workflow_event_execution_read.py +1 -0
- vellum/client/types/workflow_execution_detail.py +1 -0
- vellum/client/types/workflow_execution_fulfilled_event.py +1 -0
- vellum/client/types/workflow_execution_initiated_event.py +1 -0
- vellum/client/types/workflow_execution_paused_event.py +1 -0
- vellum/client/types/workflow_execution_rejected_event.py +1 -0
- vellum/client/types/workflow_execution_resumed_event.py +1 -0
- vellum/client/types/workflow_execution_snapshotted_event.py +1 -0
- vellum/client/types/workflow_execution_span.py +1 -0
- vellum/client/types/workflow_execution_span_attributes.py +1 -0
- vellum/client/types/workflow_execution_streaming_event.py +1 -0
- vellum/client/types/workflow_parent_context.py +1 -0
- vellum/client/types/workflow_sandbox_parent_context.py +1 -0
- vellum/{resources/release_reviews/__init__.py → types/external_parent_context.py} +1 -1
- vellum/workflows/descriptors/base.py +31 -1
- vellum/workflows/descriptors/utils.py +19 -1
- vellum/workflows/emitters/vellum_emitter.py +3 -2
- vellum/workflows/events/types.py +6 -0
- vellum/workflows/expressions/accessor.py +23 -15
- vellum/workflows/expressions/add.py +41 -0
- vellum/workflows/expressions/length.py +35 -0
- vellum/workflows/expressions/minus.py +41 -0
- vellum/workflows/expressions/tests/test_add.py +72 -0
- vellum/workflows/expressions/tests/test_length.py +38 -0
- vellum/workflows/expressions/tests/test_minus.py +72 -0
- vellum/workflows/integrations/composio_service.py +4 -0
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +1 -1
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +2 -2
- vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +5 -15
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +12 -1
- vellum/workflows/nodes/displayable/tool_calling_node/state.py +2 -0
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py +49 -0
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +3 -8
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +167 -50
- vellum/workflows/state/context.py +13 -2
- vellum/workflows/types/definition.py +3 -8
- vellum/workflows/types/tests/test_definition.py +3 -4
- vellum/workflows/utils/functions.py +1 -1
- vellum/workflows/utils/tests/test_functions.py +3 -3
- {vellum_ai-1.0.10.dist-info → vellum_ai-1.1.0.dist-info}/METADATA +1 -1
- {vellum_ai-1.0.10.dist-info → vellum_ai-1.1.0.dist-info}/RECORD +73 -68
- vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +93 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +0 -4
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +98 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +1 -1
- vellum_ee/workflows/display/utils/expressions.py +13 -1
- vellum/client/resources/release_reviews/__init__.py +0 -2
- vellum/client/resources/release_reviews/client.py +0 -139
- vellum/resources/release_reviews/client.py +0 -3
- {vellum_ai-1.0.10.dist-info → vellum_ai-1.1.0.dist-info}/LICENSE +0 -0
- {vellum_ai-1.0.10.dist-info → vellum_ai-1.1.0.dist-info}/WHEEL +0 -0
- {vellum_ai-1.0.10.dist-info → vellum_ai-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
from uuid import uuid4
|
3
|
-
from typing import Any, Iterator
|
3
|
+
from typing import Any, Iterator
|
4
4
|
|
5
5
|
from vellum import ChatMessage
|
6
6
|
from vellum.client.types.fulfilled_execute_prompt_event import FulfilledExecutePromptEvent
|
@@ -15,6 +15,7 @@ from vellum.workflows import BaseWorkflow
|
|
15
15
|
from vellum.workflows.inputs.base import BaseInputs
|
16
16
|
from vellum.workflows.nodes.bases import BaseNode
|
17
17
|
from vellum.workflows.nodes.displayable.tool_calling_node.node import ToolCallingNode
|
18
|
+
from vellum.workflows.nodes.displayable.tool_calling_node.state import ToolCallingState
|
18
19
|
from vellum.workflows.nodes.displayable.tool_calling_node.utils import create_function_node, create_tool_router_node
|
19
20
|
from vellum.workflows.outputs.base import BaseOutputs
|
20
21
|
from vellum.workflows.state.base import BaseState, StateMeta
|
@@ -44,7 +45,7 @@ def test_port_condition_match_function_name():
|
|
44
45
|
)
|
45
46
|
|
46
47
|
# AND a state with a function call to the first function
|
47
|
-
state =
|
48
|
+
state = ToolCallingState(
|
48
49
|
meta=StateMeta(
|
49
50
|
node_outputs={
|
50
51
|
router_node.Outputs.results: [
|
@@ -116,12 +117,6 @@ def test_tool_calling_node_inline_workflow_context():
|
|
116
117
|
)
|
117
118
|
function_node._context = parent_context
|
118
119
|
|
119
|
-
# Create a state with chat_history for the function node
|
120
|
-
class TestState(BaseState):
|
121
|
-
chat_history: List[ChatMessage] = []
|
122
|
-
|
123
|
-
function_node.state = TestState(meta=StateMeta(node_outputs={tool_router_node.Outputs.text: '{"arguments": {}}'}))
|
124
|
-
|
125
120
|
# WHEN the function node runs
|
126
121
|
outputs = list(function_node.run())
|
127
122
|
|
@@ -5,6 +5,8 @@ from typing import Any, Callable, Dict, Iterator, List, Optional, Type, Union, c
|
|
5
5
|
from pydash import snake_case
|
6
6
|
|
7
7
|
from vellum import ChatMessage, PromptBlock
|
8
|
+
from vellum.client.types.array_chat_message_content import ArrayChatMessageContent
|
9
|
+
from vellum.client.types.array_chat_message_content_item import ArrayChatMessageContentItem
|
8
10
|
from vellum.client.types.function_call_chat_message_content import FunctionCallChatMessageContent
|
9
11
|
from vellum.client.types.function_call_chat_message_content_value import FunctionCallChatMessageContentValue
|
10
12
|
from vellum.client.types.function_definition import FunctionDefinition
|
@@ -59,6 +61,9 @@ class FunctionCallNodeMixin:
|
|
59
61
|
content=StringChatMessageContent(value=json.dumps(result, cls=DefaultStateEncoder)),
|
60
62
|
)
|
61
63
|
)
|
64
|
+
with state.__quiet__():
|
65
|
+
state.current_function_calls_processed += 1
|
66
|
+
state.current_prompt_output_index += 1
|
62
67
|
|
63
68
|
|
64
69
|
class ToolRouterNode(InlinePromptNode[ToolCallingState]):
|
@@ -73,32 +78,52 @@ class ToolRouterNode(InlinePromptNode[ToolCallingState]):
|
|
73
78
|
raise NodeException(message=max_iterations_message, code=WorkflowErrorCode.NODE_EXECUTION)
|
74
79
|
|
75
80
|
generator = super().run()
|
81
|
+
with self.state.__quiet__():
|
82
|
+
self.state.current_prompt_output_index = 0
|
83
|
+
self.state.current_function_calls_processed = 0
|
84
|
+
self.state.prompt_iterations += 1
|
76
85
|
for output in generator:
|
77
|
-
if output.name ==
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
content=FunctionCallChatMessageContent(
|
91
|
-
value=FunctionCallChatMessageContentValue(
|
92
|
-
name=function_call.name,
|
93
|
-
arguments=function_call.arguments,
|
94
|
-
id=function_call.id,
|
95
|
-
),
|
96
|
-
),
|
97
|
-
)
|
86
|
+
if output.name == InlinePromptNode.Outputs.results.name and output.value:
|
87
|
+
prompt_outputs = cast(List[PromptOutput], output.value)
|
88
|
+
chat_contents: List[ArrayChatMessageContentItem] = []
|
89
|
+
for prompt_output in prompt_outputs:
|
90
|
+
if prompt_output.type == "STRING":
|
91
|
+
chat_contents.append(StringChatMessageContent(value=prompt_output.value))
|
92
|
+
elif prompt_output.type == "FUNCTION_CALL" and prompt_output.value:
|
93
|
+
raw_function_call = prompt_output.value.model_dump()
|
94
|
+
if "state" in raw_function_call:
|
95
|
+
del raw_function_call["state"]
|
96
|
+
chat_contents.append(
|
97
|
+
FunctionCallChatMessageContent(
|
98
|
+
value=FunctionCallChatMessageContentValue.model_validate(raw_function_call)
|
98
99
|
)
|
100
|
+
)
|
101
|
+
|
102
|
+
if len(chat_contents) == 1:
|
103
|
+
if chat_contents[0].type == "STRING":
|
104
|
+
self.state.chat_history.append(ChatMessage(role="ASSISTANT", text=chat_contents[0].value))
|
105
|
+
else:
|
106
|
+
self.state.chat_history.append(ChatMessage(role="ASSISTANT", content=chat_contents[0]))
|
107
|
+
else:
|
108
|
+
self.state.chat_history.append(
|
109
|
+
ChatMessage(role="ASSISTANT", content=ArrayChatMessageContent(value=chat_contents))
|
110
|
+
)
|
111
|
+
|
99
112
|
yield output
|
100
113
|
|
101
114
|
|
115
|
+
class RouterNode(BaseNode[ToolCallingState]):
|
116
|
+
"""Router node that handles routing to function nodes based on outputs."""
|
117
|
+
|
118
|
+
class Trigger(BaseNode.Trigger):
|
119
|
+
merge_behavior = MergeBehavior.AWAIT_ATTRIBUTES
|
120
|
+
|
121
|
+
def run(self) -> Iterator[BaseOutput]:
|
122
|
+
# Router node doesn't process outputs or create chat messages
|
123
|
+
# It just handles the routing logic via its ports
|
124
|
+
yield from []
|
125
|
+
|
126
|
+
|
102
127
|
class DynamicSubworkflowDeploymentNode(SubworkflowDeploymentNode[ToolCallingState], FunctionCallNodeMixin):
|
103
128
|
"""Node that executes a deployment definition with function call output."""
|
104
129
|
|
@@ -225,42 +250,51 @@ class MCPNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
|
|
225
250
|
yield from []
|
226
251
|
|
227
252
|
|
228
|
-
|
253
|
+
class ElseNode(BaseNode[ToolCallingState]):
|
254
|
+
"""Node that executes when no function conditions match."""
|
255
|
+
|
256
|
+
class Ports(BaseNode.Ports):
|
257
|
+
# Redefined in the create_else_node function, but defined here to resolve mypy errors
|
258
|
+
loop = Port.on_if(
|
259
|
+
ToolCallingState.current_prompt_output_index.less_than(1)
|
260
|
+
| ToolCallingState.current_function_calls_processed.greater_than(0)
|
261
|
+
)
|
262
|
+
end = Port.on_else()
|
263
|
+
|
264
|
+
def run(self) -> BaseNode.Outputs:
|
265
|
+
with self.state.__quiet__():
|
266
|
+
self.state.current_prompt_output_index += 1
|
267
|
+
return self.Outputs()
|
268
|
+
|
269
|
+
|
270
|
+
def _hydrate_composio_tool_definition(tool_def: ComposioToolDefinition) -> FunctionDefinition:
|
229
271
|
"""Hydrate a ComposioToolDefinition with detailed information from the Composio API.
|
230
272
|
|
231
273
|
Args:
|
232
274
|
tool_def: The basic ComposioToolDefinition to enhance
|
233
275
|
|
234
276
|
Returns:
|
235
|
-
|
277
|
+
FunctionDefinition with detailed parameters and description
|
236
278
|
"""
|
237
279
|
try:
|
238
280
|
composio_service = ComposioService()
|
239
281
|
tool_details = composio_service.get_tool_by_slug(tool_def.action)
|
240
282
|
|
241
|
-
#
|
242
|
-
|
243
|
-
|
244
|
-
toolkit_info.get("slug", tool_def.toolkit) if isinstance(toolkit_info, dict) else tool_def.toolkit
|
245
|
-
)
|
246
|
-
|
247
|
-
# Create a version of the tool definition with proper field extraction
|
248
|
-
return ComposioToolDefinition(
|
249
|
-
type=tool_def.type,
|
250
|
-
toolkit=toolkit_slug.upper() if toolkit_slug else tool_def.toolkit,
|
251
|
-
action=tool_details.get("slug", tool_def.action),
|
283
|
+
# Create a FunctionDefinition directly with proper field extraction
|
284
|
+
return FunctionDefinition(
|
285
|
+
name=tool_def.name,
|
252
286
|
description=tool_details.get("description", tool_def.description),
|
253
|
-
|
254
|
-
parameters=tool_details.get("input_parameters", tool_def.parameters),
|
255
|
-
version=tool_details.get("version", tool_def.version),
|
256
|
-
tags=tool_details.get("tags", tool_def.tags),
|
257
|
-
user_id=tool_def.user_id,
|
287
|
+
parameters=tool_details.get("input_parameters", {}),
|
258
288
|
)
|
259
289
|
|
260
290
|
except Exception as e:
|
261
|
-
# If hydration fails (including no API key), log and return
|
291
|
+
# If hydration fails (including no API key), log and return basic function definition
|
262
292
|
logger.warning(f"Failed to enhance Composio tool '{tool_def.action}': {e}")
|
263
|
-
return
|
293
|
+
return FunctionDefinition(
|
294
|
+
name=tool_def.name,
|
295
|
+
description=tool_def.description,
|
296
|
+
parameters={},
|
297
|
+
)
|
264
298
|
|
265
299
|
|
266
300
|
def hydrate_mcp_tool_definitions(server_def: MCPServer) -> List[MCPToolDefinition]:
|
@@ -303,8 +337,13 @@ def create_tool_router_node(
|
|
303
337
|
return Port.on_if(
|
304
338
|
LazyReference(
|
305
339
|
lambda: (
|
306
|
-
node.Outputs.results
|
307
|
-
& node.Outputs.results[
|
340
|
+
ToolCallingState.current_prompt_output_index.less_than(node.Outputs.results.length())
|
341
|
+
& node.Outputs.results[ToolCallingState.current_prompt_output_index]["type"].equals(
|
342
|
+
"FUNCTION_CALL"
|
343
|
+
)
|
344
|
+
& node.Outputs.results[ToolCallingState.current_prompt_output_index]["value"]["name"].equals(
|
345
|
+
fn_name
|
346
|
+
)
|
308
347
|
)
|
309
348
|
)
|
310
349
|
)
|
@@ -313,13 +352,7 @@ def create_tool_router_node(
|
|
313
352
|
if isinstance(function, ComposioToolDefinition):
|
314
353
|
# Get Composio tool details and hydrate the function definition
|
315
354
|
enhanced_function = _hydrate_composio_tool_definition(function)
|
316
|
-
prompt_functions.append(
|
317
|
-
FunctionDefinition(
|
318
|
-
name=enhanced_function.name,
|
319
|
-
description=enhanced_function.description,
|
320
|
-
parameters=enhanced_function.parameters,
|
321
|
-
)
|
322
|
-
)
|
355
|
+
prompt_functions.append(enhanced_function)
|
323
356
|
# Create port for this function (using original function for get_function_name)
|
324
357
|
function_name = get_function_name(function)
|
325
358
|
port = create_port_condition(function_name)
|
@@ -402,6 +435,69 @@ def create_tool_router_node(
|
|
402
435
|
return node
|
403
436
|
|
404
437
|
|
438
|
+
def create_router_node(
|
439
|
+
functions: List[Tool],
|
440
|
+
tool_router_node: Type[ToolRouterNode],
|
441
|
+
) -> Type[RouterNode]:
|
442
|
+
"""Create a RouterNode with the same ports as ToolRouterNode."""
|
443
|
+
|
444
|
+
if functions and len(functions) > 0:
|
445
|
+
# Create dynamic ports and convert functions in a single loop
|
446
|
+
Ports = type("Ports", (), {})
|
447
|
+
|
448
|
+
def create_port_condition(fn_name):
|
449
|
+
return Port.on_if(
|
450
|
+
LazyReference(
|
451
|
+
lambda: (
|
452
|
+
ToolCallingState.current_prompt_output_index.less_than(
|
453
|
+
tool_router_node.Outputs.results.length()
|
454
|
+
)
|
455
|
+
& tool_router_node.Outputs.results[ToolCallingState.current_prompt_output_index]["type"].equals(
|
456
|
+
"FUNCTION_CALL"
|
457
|
+
)
|
458
|
+
& tool_router_node.Outputs.results[ToolCallingState.current_prompt_output_index]["value"][
|
459
|
+
"name"
|
460
|
+
].equals(fn_name)
|
461
|
+
)
|
462
|
+
)
|
463
|
+
)
|
464
|
+
|
465
|
+
for function in functions:
|
466
|
+
if isinstance(function, ComposioToolDefinition):
|
467
|
+
function_name = get_function_name(function)
|
468
|
+
port = create_port_condition(function_name)
|
469
|
+
setattr(Ports, function_name, port)
|
470
|
+
elif isinstance(function, MCPServer):
|
471
|
+
tool_functions: List[MCPToolDefinition] = hydrate_mcp_tool_definitions(function)
|
472
|
+
for tool_function in tool_functions:
|
473
|
+
name = get_mcp_tool_name(tool_function)
|
474
|
+
port = create_port_condition(name)
|
475
|
+
setattr(Ports, name, port)
|
476
|
+
else:
|
477
|
+
function_name = get_function_name(function)
|
478
|
+
port = create_port_condition(function_name)
|
479
|
+
setattr(Ports, function_name, port)
|
480
|
+
|
481
|
+
# Add the else port for when no function conditions match
|
482
|
+
setattr(Ports, "default", Port.on_else())
|
483
|
+
else:
|
484
|
+
# If no functions exist, create a simple Ports class with just a default port
|
485
|
+
Ports = type("Ports", (), {"default": Port(default=True)})
|
486
|
+
|
487
|
+
node = cast(
|
488
|
+
Type[RouterNode],
|
489
|
+
type(
|
490
|
+
"RouterNode",
|
491
|
+
(RouterNode,),
|
492
|
+
{
|
493
|
+
"Ports": Ports,
|
494
|
+
"__module__": __name__,
|
495
|
+
},
|
496
|
+
),
|
497
|
+
)
|
498
|
+
return node
|
499
|
+
|
500
|
+
|
405
501
|
def create_function_node(
|
406
502
|
function: ToolBase,
|
407
503
|
tool_router_node: Type[ToolRouterNode],
|
@@ -485,6 +581,27 @@ def create_mcp_tool_node(
|
|
485
581
|
return node
|
486
582
|
|
487
583
|
|
584
|
+
def create_else_node(
|
585
|
+
tool_router_node: Type[ToolRouterNode],
|
586
|
+
) -> Type[ElseNode]:
|
587
|
+
class Ports(ElseNode.Ports):
|
588
|
+
loop = Port.on_if(
|
589
|
+
ToolCallingState.current_prompt_output_index.less_than(tool_router_node.Outputs.results.length())
|
590
|
+
| ToolCallingState.current_function_calls_processed.greater_than(0)
|
591
|
+
)
|
592
|
+
end = Port.on_else()
|
593
|
+
|
594
|
+
node = type(
|
595
|
+
f"{tool_router_node.__name__}_ElseNode",
|
596
|
+
(ElseNode,),
|
597
|
+
{
|
598
|
+
"Ports": Ports,
|
599
|
+
"__module__": __name__,
|
600
|
+
},
|
601
|
+
)
|
602
|
+
return node
|
603
|
+
|
604
|
+
|
488
605
|
def get_function_name(function: ToolBase) -> str:
|
489
606
|
if isinstance(function, DeploymentDefinition):
|
490
607
|
name = str(function.deployment_id or function.deployment_name)
|
@@ -1,9 +1,11 @@
|
|
1
1
|
from functools import cached_property
|
2
2
|
from queue import Queue
|
3
|
+
from uuid import uuid4
|
3
4
|
from typing import TYPE_CHECKING, Dict, List, Optional, Type
|
4
5
|
|
5
6
|
from vellum import Vellum
|
6
7
|
from vellum.workflows.context import ExecutionContext, get_execution_context
|
8
|
+
from vellum.workflows.events.types import ExternalParentContext
|
7
9
|
from vellum.workflows.nodes.mocks import MockNodeExecution, MockNodeExecutionArg
|
8
10
|
from vellum.workflows.outputs.base import BaseOutputs
|
9
11
|
from vellum.workflows.references.constant import ConstantValueReference
|
@@ -25,8 +27,17 @@ class WorkflowContext:
|
|
25
27
|
self._event_queue: Optional[Queue["WorkflowEvent"]] = None
|
26
28
|
self._node_output_mocks_map: Dict[Type[BaseOutputs], List[MockNodeExecution]] = {}
|
27
29
|
self._execution_context = get_execution_context()
|
28
|
-
|
29
|
-
|
30
|
+
|
31
|
+
if execution_context is not None:
|
32
|
+
|
33
|
+
self._execution_context.trace_id = execution_context.trace_id
|
34
|
+
|
35
|
+
if execution_context.parent_context is not None:
|
36
|
+
self._execution_context.parent_context = execution_context.parent_context
|
37
|
+
|
38
|
+
if self._execution_context.parent_context is None:
|
39
|
+
self._execution_context.parent_context = ExternalParentContext(span_id=uuid4())
|
40
|
+
|
30
41
|
self._generated_files = generated_files
|
31
42
|
|
32
43
|
@cached_property
|
@@ -2,13 +2,12 @@ import importlib
|
|
2
2
|
import inspect
|
3
3
|
from types import FrameType
|
4
4
|
from uuid import UUID
|
5
|
-
from typing import Annotated, Any, Dict,
|
5
|
+
from typing import Annotated, Any, Dict, Literal, Optional, Union
|
6
6
|
|
7
7
|
from pydantic import BeforeValidator
|
8
8
|
|
9
9
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
10
10
|
from vellum.client.types.code_resource_definition import CodeResourceDefinition as ClientCodeResourceDefinition
|
11
|
-
from vellum.client.types.vellum_secret import VellumSecret
|
12
11
|
from vellum.workflows.constants import AuthorizationType
|
13
12
|
from vellum.workflows.references.environment_variable import EnvironmentVariableReference
|
14
13
|
|
@@ -111,11 +110,6 @@ class ComposioToolDefinition(UniversalBaseModel):
|
|
111
110
|
toolkit: str # "GITHUB", "SLACK", etc.
|
112
111
|
action: str # Specific action like "GITHUB_CREATE_AN_ISSUE"
|
113
112
|
description: str
|
114
|
-
|
115
|
-
display_name: Optional[str] = None
|
116
|
-
parameters: Optional[Dict[str, Any]] = None
|
117
|
-
version: Optional[str] = None
|
118
|
-
tags: Optional[List[str]] = None
|
119
113
|
user_id: Optional[str] = None
|
120
114
|
|
121
115
|
@property
|
@@ -125,12 +119,13 @@ class ComposioToolDefinition(UniversalBaseModel):
|
|
125
119
|
|
126
120
|
|
127
121
|
class MCPServer(UniversalBaseModel):
|
122
|
+
type: Literal["MCP_SERVER"] = "MCP_SERVER"
|
128
123
|
name: str
|
129
124
|
url: str
|
130
125
|
authorization_type: AuthorizationType = AuthorizationType.BEARER_TOKEN
|
131
126
|
bearer_token_value: Optional[Union[str, EnvironmentVariableReference]] = None
|
132
127
|
api_key_header_key: Optional[str] = None
|
133
|
-
api_key_header_value: Optional[Union[str,
|
128
|
+
api_key_header_value: Optional[Union[str, EnvironmentVariableReference]] = None
|
134
129
|
|
135
130
|
model_config = {"arbitrary_types_allowed": True}
|
136
131
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import pytest
|
2
2
|
from uuid import UUID
|
3
3
|
|
4
|
-
from vellum.client.types.vellum_secret import VellumSecret
|
5
4
|
from vellum.workflows.constants import AuthorizationType
|
6
5
|
from vellum.workflows.references.environment_variable import EnvironmentVariableReference
|
7
6
|
from vellum.workflows.types.definition import ComposioToolDefinition, DeploymentDefinition, MCPServer, MCPToolDefinition
|
@@ -48,7 +47,7 @@ def test_composio_tool_definition_creation():
|
|
48
47
|
assert composio_tool.toolkit == "GITHUB"
|
49
48
|
assert composio_tool.action == "GITHUB_CREATE_AN_ISSUE"
|
50
49
|
assert composio_tool.description == "Create a new issue in a GitHub repository"
|
51
|
-
assert composio_tool.
|
50
|
+
assert composio_tool.user_id is None
|
52
51
|
assert composio_tool.name == "github_create_an_issue"
|
53
52
|
|
54
53
|
|
@@ -98,7 +97,7 @@ def test_mcp_tool_definition_creation_api_key():
|
|
98
97
|
url="https://api.githubcopilot.com/mcp/",
|
99
98
|
authorization_type=AuthorizationType.API_KEY,
|
100
99
|
api_key_header_key="Authorization",
|
101
|
-
api_key_header_value=
|
100
|
+
api_key_header_value=EnvironmentVariableReference(name="GITHUB_PERSONAL_ACCESS_TOKEN"),
|
102
101
|
),
|
103
102
|
parameters={
|
104
103
|
"type": "object",
|
@@ -115,7 +114,7 @@ def test_mcp_tool_definition_creation_api_key():
|
|
115
114
|
assert mcp_tool.server.url == "https://api.githubcopilot.com/mcp/"
|
116
115
|
assert mcp_tool.server.authorization_type == AuthorizationType.API_KEY
|
117
116
|
assert mcp_tool.server.api_key_header_key == "Authorization"
|
118
|
-
assert isinstance(mcp_tool.server.api_key_header_value,
|
117
|
+
assert isinstance(mcp_tool.server.api_key_header_value, EnvironmentVariableReference)
|
119
118
|
assert mcp_tool.server.api_key_header_value.name == "GITHUB_PERSONAL_ACCESS_TOKEN"
|
120
119
|
assert mcp_tool.parameters == {
|
121
120
|
"type": "object",
|
@@ -221,7 +221,7 @@ def compile_workflow_deployment_function_definition(
|
|
221
221
|
deployment = deployment_config["deployment"]
|
222
222
|
release_tag = deployment_config["release_tag"]
|
223
223
|
|
224
|
-
workflow_deployment_release = vellum_client.
|
224
|
+
workflow_deployment_release = vellum_client.workflow_deployments.retrieve_workflow_deployment_release(
|
225
225
|
deployment, release_tag
|
226
226
|
)
|
227
227
|
|
@@ -448,7 +448,7 @@ def test_compile_workflow_deployment_function_definition__just_name():
|
|
448
448
|
mock_release = Mock()
|
449
449
|
mock_release.workflow_version.input_variables = []
|
450
450
|
mock_release.description = "This is a test deployment"
|
451
|
-
mock_client.
|
451
|
+
mock_client.workflow_deployments.retrieve_workflow_deployment_release.return_value = mock_release
|
452
452
|
|
453
453
|
deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
|
454
454
|
|
@@ -494,7 +494,7 @@ def test_compile_workflow_deployment_function_definition__all_args():
|
|
494
494
|
|
495
495
|
mock_release.workflow_version.input_variables = mock_inputs
|
496
496
|
mock_release.description = "This is a test deployment"
|
497
|
-
mock_client.
|
497
|
+
mock_client.workflow_deployments.retrieve_workflow_deployment_release.return_value = mock_release
|
498
498
|
|
499
499
|
deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
|
500
500
|
|
@@ -562,7 +562,7 @@ def test_compile_workflow_deployment_function_definition__defaults():
|
|
562
562
|
|
563
563
|
mock_release.workflow_version.input_variables = mock_inputs
|
564
564
|
mock_release.description = "This is a test deployment"
|
565
|
-
mock_client.
|
565
|
+
mock_client.workflow_deployments.retrieve_workflow_deployment_release.return_value = mock_release
|
566
566
|
|
567
567
|
deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
|
568
568
|
|