vellum-ai 0.14.70__py3-none-any.whl → 0.14.72__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 +1 -1
- vellum/plugins/vellum_mypy.py +1 -1
- vellum/workflows/environment/environment.py +5 -2
- vellum/workflows/events/__init__.py +2 -0
- vellum/workflows/events/stream.py +28 -0
- vellum/workflows/events/workflow.py +3 -2
- vellum/workflows/nodes/displayable/code_execution_node/node.py +8 -1
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +53 -0
- vellum/workflows/nodes/experimental/tool_calling_node/node.py +1 -1
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py +2 -2
- vellum/workflows/references/environment_variable.py +11 -8
- vellum/workflows/runner/runner.py +21 -3
- vellum/workflows/utils/functions.py +55 -1
- vellum/workflows/utils/tests/test_functions.py +151 -1
- vellum/workflows/utils/tests/test_vellum_variables.py +25 -1
- vellum/workflows/utils/vellum_variables.py +30 -0
- vellum/workflows/workflows/base.py +11 -5
- {vellum_ai-0.14.70.dist-info → vellum_ai-0.14.72.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.70.dist-info → vellum_ai-0.14.72.dist-info}/RECORD +34 -31
- vellum_cli/image_push.py +1 -5
- vellum_cli/push.py +7 -0
- vellum_cli/tests/test_image_push.py +1 -2
- vellum_cli/tests/test_push.py +33 -2
- vellum_ee/workflows/display/nodes/utils.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +7 -3
- vellum_ee/workflows/display/nodes/vellum/search_node.py +69 -6
- vellum_ee/workflows/display/nodes/vellum/tests/test_search_node.py +104 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +82 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +4 -4
- vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py +37 -0
- vellum_ee/workflows/display/utils/expressions.py +10 -1
- {vellum_ai-0.14.70.dist-info → vellum_ai-0.14.72.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.70.dist-info → vellum_ai-0.14.72.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.70.dist-info → vellum_ai-0.14.72.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
|
|
18
18
|
headers: typing.Dict[str, str] = {
|
19
19
|
"X-Fern-Language": "Python",
|
20
20
|
"X-Fern-SDK-Name": "vellum-ai",
|
21
|
-
"X-Fern-SDK-Version": "0.14.
|
21
|
+
"X-Fern-SDK-Version": "0.14.72",
|
22
22
|
}
|
23
23
|
headers["X-API-KEY"] = self.api_key
|
24
24
|
return headers
|
vellum/plugins/vellum_mypy.py
CHANGED
@@ -573,7 +573,7 @@ class VellumMypyPlugin(Plugin):
|
|
573
573
|
alias_target = alias.target
|
574
574
|
if (
|
575
575
|
not isinstance(alias_target, Instance)
|
576
|
-
or not _is_subclass(alias_target.type, "
|
576
|
+
or not _is_subclass(alias_target.type, "vellum.workflows.events.stream.WorkflowEventGenerator")
|
577
577
|
or not alias_target.args
|
578
578
|
):
|
579
579
|
return ctx.default_return_type
|
@@ -5,8 +5,11 @@ from vellum.workflows.references import EnvironmentVariableReference
|
|
5
5
|
|
6
6
|
class EnvironmentVariables:
|
7
7
|
@staticmethod
|
8
|
-
def get(name: str, default: Optional[str] = None)
|
9
|
-
|
8
|
+
def get(name: str, default: Optional[str] = None):
|
9
|
+
env_ref = EnvironmentVariableReference(name=name)
|
10
|
+
if default is not None:
|
11
|
+
return env_ref.coalesce(default)
|
12
|
+
return env_ref
|
10
13
|
|
11
14
|
|
12
15
|
# Deprecated: Use EnvironmentVariables instead. Will be removed in v0.15.0
|
@@ -5,6 +5,7 @@ from .node import (
|
|
5
5
|
NodeExecutionRejectedEvent,
|
6
6
|
NodeExecutionStreamingEvent,
|
7
7
|
)
|
8
|
+
from .stream import WorkflowEventGenerator
|
8
9
|
from .workflow import (
|
9
10
|
WorkflowEvent,
|
10
11
|
WorkflowEventStream,
|
@@ -26,4 +27,5 @@ __all__ = [
|
|
26
27
|
"WorkflowExecutionStreamingEvent",
|
27
28
|
"WorkflowEvent",
|
28
29
|
"WorkflowEventStream",
|
30
|
+
"WorkflowEventGenerator",
|
29
31
|
]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from uuid import UUID
|
2
|
+
from typing import Generator, Generic, Iterator, TypeVar
|
3
|
+
|
4
|
+
EventType = TypeVar("EventType")
|
5
|
+
|
6
|
+
|
7
|
+
class WorkflowEventGenerator(Generic[EventType]):
|
8
|
+
"""
|
9
|
+
Generic wrapper for event streams that exposes span_id as a top-level property
|
10
|
+
while maintaining iterator compatibility.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, event_generator: Generator[EventType, None, None], span_id: UUID):
|
14
|
+
self._event_generator = event_generator
|
15
|
+
self._span_id = span_id
|
16
|
+
|
17
|
+
@property
|
18
|
+
def span_id(self) -> UUID:
|
19
|
+
"""The span_id associated with this workflow stream."""
|
20
|
+
return self._span_id
|
21
|
+
|
22
|
+
def __iter__(self) -> Iterator[EventType]:
|
23
|
+
"""Return self to make this object iterable."""
|
24
|
+
return self
|
25
|
+
|
26
|
+
def __next__(self) -> EventType:
|
27
|
+
"""Get the next event from the underlying generator."""
|
28
|
+
return next(self._event_generator)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import TYPE_CHECKING, Any, Dict,
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, Generic, Iterable, Literal, Optional, Type, Union
|
3
3
|
from typing_extensions import TypeGuard
|
4
4
|
|
5
5
|
from pydantic import field_serializer
|
@@ -19,6 +19,7 @@ from .node import (
|
|
19
19
|
NodeExecutionResumedEvent,
|
20
20
|
NodeExecutionStreamingEvent,
|
21
21
|
)
|
22
|
+
from .stream import WorkflowEventGenerator
|
22
23
|
from .types import BaseEvent, default_serializer
|
23
24
|
|
24
25
|
if TYPE_CHECKING:
|
@@ -193,7 +194,7 @@ WorkflowEvent = Union[
|
|
193
194
|
WorkflowExecutionSnapshottedEvent,
|
194
195
|
]
|
195
196
|
|
196
|
-
WorkflowEventStream =
|
197
|
+
WorkflowEventStream = WorkflowEventGenerator[WorkflowEvent]
|
197
198
|
|
198
199
|
WorkflowExecutionEvent = Union[
|
199
200
|
WorkflowExecutionInitiatedEvent,
|
@@ -98,7 +98,7 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
|
|
98
98
|
def run(self) -> Outputs:
|
99
99
|
output_type = self.__class__.get_output_type()
|
100
100
|
code, filepath = self._resolve_code()
|
101
|
-
if not self.packages and self.runtime == "PYTHON_3_11_6":
|
101
|
+
if not self.packages and self.runtime == "PYTHON_3_11_6" and not self._has_secrets_in_code_inputs():
|
102
102
|
logs, result = run_code_inline(code, self.code_inputs, output_type, filepath)
|
103
103
|
return self.Outputs(result=result, log=logs)
|
104
104
|
|
@@ -133,6 +133,13 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
|
|
133
133
|
|
134
134
|
return self.Outputs(result=code_execution_result.output.value, log=code_execution_result.log)
|
135
135
|
|
136
|
+
def _has_secrets_in_code_inputs(self) -> bool:
|
137
|
+
"""Check if any code_inputs contain VellumSecret instances that require API execution."""
|
138
|
+
for input_value in self.code_inputs.values():
|
139
|
+
if isinstance(input_value, VellumSecret):
|
140
|
+
return True
|
141
|
+
return False
|
142
|
+
|
136
143
|
def _compile_code_inputs(self) -> List[CodeExecutorInput]:
|
137
144
|
# TODO: We may want to consolidate with prompt deployment input compilation
|
138
145
|
# https://app.shortcut.com/vellum/story/4117
|
@@ -1233,3 +1233,56 @@ def main(numbers: List[str]) -> str:
|
|
1233
1233
|
],
|
1234
1234
|
request_options=None,
|
1235
1235
|
)
|
1236
|
+
|
1237
|
+
|
1238
|
+
def test_run_node__vellum_secret_forces_api_execution(vellum_client):
|
1239
|
+
"""Test that CodeExecutionNode with VellumSecretReference forces API execution instead of inline execution."""
|
1240
|
+
|
1241
|
+
# GIVEN a node that subclasses CodeExecutionNode with VellumSecretReference but no packages
|
1242
|
+
# This should normally run inline, but the presence of secrets should force API execution
|
1243
|
+
class State(BaseState):
|
1244
|
+
pass
|
1245
|
+
|
1246
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[State, str]):
|
1247
|
+
code = """
|
1248
|
+
def main(secret: str) -> str:
|
1249
|
+
return f"Secret value: {secret}"
|
1250
|
+
"""
|
1251
|
+
runtime = "PYTHON_3_11_6"
|
1252
|
+
# Note: No packages specified, which would normally trigger inline execution
|
1253
|
+
|
1254
|
+
code_inputs = {
|
1255
|
+
"secret": VellumSecretReference("MY_SECRET"),
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
# AND we know what the Code Execution Node will respond with
|
1259
|
+
mock_code_execution = CodeExecutorResponse(
|
1260
|
+
log="",
|
1261
|
+
output=StringVellumValue(value="Secret value: my_secret_value"),
|
1262
|
+
)
|
1263
|
+
vellum_client.execute_code.return_value = mock_code_execution
|
1264
|
+
|
1265
|
+
# WHEN we run the node
|
1266
|
+
node = ExampleCodeExecutionNode(state=State())
|
1267
|
+
outputs = node.run()
|
1268
|
+
|
1269
|
+
# THEN the node should have produced the outputs we expect
|
1270
|
+
assert outputs == {"result": "Secret value: my_secret_value", "log": ""}
|
1271
|
+
|
1272
|
+
# AND we should have invoked the Code via API (not inline) due to the secret
|
1273
|
+
vellum_client.execute_code.assert_called_once_with(
|
1274
|
+
input_values=[
|
1275
|
+
CodeExecutorSecretInput(
|
1276
|
+
name="secret",
|
1277
|
+
value="MY_SECRET",
|
1278
|
+
)
|
1279
|
+
],
|
1280
|
+
code="""
|
1281
|
+
def main(secret: str) -> str:
|
1282
|
+
return f"Secret value: {secret}"
|
1283
|
+
""",
|
1284
|
+
runtime="PYTHON_3_11_6",
|
1285
|
+
output_type="STRING",
|
1286
|
+
packages=[],
|
1287
|
+
request_options=None,
|
1288
|
+
)
|
@@ -110,7 +110,7 @@ class ToolCallingNode(BaseNode):
|
|
110
110
|
|
111
111
|
# Get configuration for this function
|
112
112
|
config = {}
|
113
|
-
if self.function_configs and function.__name__ in self.function_configs:
|
113
|
+
if callable(function) and self.function_configs and function.__name__ in self.function_configs:
|
114
114
|
config = self.function_configs[function.__name__]
|
115
115
|
|
116
116
|
packages = config.get("packages", None)
|
@@ -145,7 +145,7 @@ def create_function_node(
|
|
145
145
|
"""
|
146
146
|
if is_workflow_class(function):
|
147
147
|
# Create a class-level wrapper that calls the original function
|
148
|
-
def
|
148
|
+
def execute_inline_workflow_function(self) -> BaseNode.Outputs:
|
149
149
|
outputs = self.state.meta.node_outputs.get(tool_router_node.Outputs.text)
|
150
150
|
|
151
151
|
outputs = json.loads(outputs)
|
@@ -181,7 +181,7 @@ def create_function_node(
|
|
181
181
|
f"InlineWorkflowNode_{function.__name__}",
|
182
182
|
(FunctionNode,),
|
183
183
|
{
|
184
|
-
"run":
|
184
|
+
"run": execute_inline_workflow_function,
|
185
185
|
"__module__": __name__,
|
186
186
|
},
|
187
187
|
)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import os
|
2
|
-
from typing import TYPE_CHECKING, Optional
|
2
|
+
from typing import TYPE_CHECKING, Any, Optional
|
3
3
|
|
4
|
+
from vellum.workflows.constants import undefined
|
4
5
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
5
6
|
|
6
7
|
if TYPE_CHECKING:
|
@@ -8,16 +9,18 @@ if TYPE_CHECKING:
|
|
8
9
|
|
9
10
|
|
10
11
|
class EnvironmentVariableReference(BaseDescriptor[str]):
|
11
|
-
def __init__(
|
12
|
+
def __init__(
|
13
|
+
self,
|
14
|
+
*,
|
15
|
+
name: str,
|
16
|
+
# DEPRECATED - to be removed in 0.15.0 release
|
17
|
+
default: Optional[str] = None,
|
18
|
+
):
|
12
19
|
super().__init__(name=name, types=(str,))
|
13
|
-
self._default = default
|
14
20
|
|
15
|
-
def resolve(self, state: "BaseState") ->
|
21
|
+
def resolve(self, state: "BaseState") -> Any:
|
16
22
|
env_value = os.environ.get(self.name)
|
17
23
|
if env_value is not None:
|
18
24
|
return env_value
|
19
25
|
|
20
|
-
|
21
|
-
return self._default
|
22
|
-
|
23
|
-
return ""
|
26
|
+
return undefined
|
@@ -5,7 +5,21 @@ import logging
|
|
5
5
|
from queue import Empty, Queue
|
6
6
|
from threading import Event as ThreadingEvent, Thread
|
7
7
|
from uuid import UUID, uuid4
|
8
|
-
from typing import
|
8
|
+
from typing import (
|
9
|
+
TYPE_CHECKING,
|
10
|
+
Any,
|
11
|
+
Dict,
|
12
|
+
Generator,
|
13
|
+
Generic,
|
14
|
+
Iterable,
|
15
|
+
Iterator,
|
16
|
+
Optional,
|
17
|
+
Sequence,
|
18
|
+
Set,
|
19
|
+
Tuple,
|
20
|
+
Type,
|
21
|
+
Union,
|
22
|
+
)
|
9
23
|
|
10
24
|
from vellum.workflows.constants import undefined
|
11
25
|
from vellum.workflows.context import ExecutionContext, execution_context, get_execution_context
|
@@ -17,7 +31,7 @@ from vellum.workflows.events import (
|
|
17
31
|
NodeExecutionRejectedEvent,
|
18
32
|
NodeExecutionStreamingEvent,
|
19
33
|
WorkflowEvent,
|
20
|
-
|
34
|
+
WorkflowEventGenerator,
|
21
35
|
WorkflowExecutionFulfilledEvent,
|
22
36
|
WorkflowExecutionInitiatedEvent,
|
23
37
|
WorkflowExecutionRejectedEvent,
|
@@ -31,6 +45,7 @@ from vellum.workflows.events.node import (
|
|
31
45
|
)
|
32
46
|
from vellum.workflows.events.types import BaseEvent, NodeParentContext, ParentContext, WorkflowParentContext
|
33
47
|
from vellum.workflows.events.workflow import (
|
48
|
+
WorkflowEventStream,
|
34
49
|
WorkflowExecutionFulfilledBody,
|
35
50
|
WorkflowExecutionInitiatedBody,
|
36
51
|
WorkflowExecutionPausedBody,
|
@@ -730,7 +745,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
730
745
|
return event.workflow_definition == self.workflow.__class__
|
731
746
|
return False
|
732
747
|
|
733
|
-
def
|
748
|
+
def _generate_events(self) -> Generator[WorkflowEvent, None, None]:
|
734
749
|
background_thread = Thread(
|
735
750
|
target=self._run_background_thread,
|
736
751
|
name=f"{self.workflow.__class__.__name__}.background_thread",
|
@@ -788,3 +803,6 @@ class WorkflowRunner(Generic[StateType]):
|
|
788
803
|
|
789
804
|
self._background_thread_queue.put(None)
|
790
805
|
cancel_thread_kill_switch.set()
|
806
|
+
|
807
|
+
def stream(self) -> WorkflowEventStream:
|
808
|
+
return WorkflowEventGenerator(self._generate_events(), self._initial_state.meta.span_id)
|
@@ -1,12 +1,14 @@
|
|
1
1
|
import dataclasses
|
2
2
|
import inspect
|
3
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional, Type, Union, get_args, get_origin
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Type, Union, get_args, get_origin
|
4
4
|
|
5
5
|
from pydantic import BaseModel
|
6
6
|
from pydantic_core import PydanticUndefined
|
7
7
|
from pydash import snake_case
|
8
8
|
|
9
|
+
from vellum import Vellum
|
9
10
|
from vellum.client.types.function_definition import FunctionDefinition
|
11
|
+
from vellum.workflows.utils.vellum_variables import vellum_variable_type_to_openapi_type
|
10
12
|
|
11
13
|
if TYPE_CHECKING:
|
12
14
|
from vellum.workflows.workflows.base import BaseWorkflow
|
@@ -86,6 +88,19 @@ def _compile_default_value(default: Any) -> Any:
|
|
86
88
|
return default
|
87
89
|
|
88
90
|
|
91
|
+
def _compile_deployment_workflow_input(input_var: Any) -> dict[str, Any]:
|
92
|
+
"""
|
93
|
+
Converts a deployment workflow input variable to a JSON schema type definition.
|
94
|
+
"""
|
95
|
+
primitive_type = vellum_variable_type_to_openapi_type(input_var.type)
|
96
|
+
input_schema = {"type": primitive_type}
|
97
|
+
|
98
|
+
if input_var.default is not None:
|
99
|
+
input_schema["default"] = input_var.default.value
|
100
|
+
|
101
|
+
return input_schema
|
102
|
+
|
103
|
+
|
89
104
|
def compile_function_definition(function: Callable) -> FunctionDefinition:
|
90
105
|
"""
|
91
106
|
Converts a Python function into our Vellum-native FunctionDefinition type.
|
@@ -151,3 +166,42 @@ def compile_workflow_function_definition(workflow_class: Type["BaseWorkflow"]) -
|
|
151
166
|
description=workflow_class.__doc__,
|
152
167
|
parameters=parameters,
|
153
168
|
)
|
169
|
+
|
170
|
+
|
171
|
+
def compile_deployment_workflow_function_definition(
|
172
|
+
deployment_config: Dict[str, str],
|
173
|
+
vellum_client: Vellum,
|
174
|
+
) -> FunctionDefinition:
|
175
|
+
"""
|
176
|
+
Converts a deployment workflow config into our Vellum-native FunctionDefinition type.
|
177
|
+
|
178
|
+
Args:
|
179
|
+
deployment_config: Dict with 'deployment' and 'release_tag' keys
|
180
|
+
vellum_client: Vellum client instance
|
181
|
+
"""
|
182
|
+
deployment = deployment_config["deployment"]
|
183
|
+
release_tag = deployment_config["release_tag"]
|
184
|
+
|
185
|
+
workflow_deployment_release = vellum_client.release_reviews.retrieve_workflow_deployment_release(
|
186
|
+
deployment, release_tag
|
187
|
+
)
|
188
|
+
|
189
|
+
input_variables = workflow_deployment_release.workflow_version.input_variables
|
190
|
+
description = workflow_deployment_release.description
|
191
|
+
|
192
|
+
properties = {}
|
193
|
+
required = []
|
194
|
+
|
195
|
+
for input_var in input_variables:
|
196
|
+
properties[input_var.key] = _compile_deployment_workflow_input(input_var)
|
197
|
+
|
198
|
+
if input_var.required and input_var.default is None:
|
199
|
+
required.append(input_var.key)
|
200
|
+
|
201
|
+
parameters = {"type": "object", "properties": properties, "required": required}
|
202
|
+
|
203
|
+
return FunctionDefinition(
|
204
|
+
name=deployment,
|
205
|
+
description=description,
|
206
|
+
parameters=parameters,
|
207
|
+
)
|
@@ -1,14 +1,21 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
+
from unittest.mock import Mock
|
2
3
|
from typing import Dict, List, Optional, Union
|
3
4
|
|
4
5
|
from pydantic import BaseModel
|
5
6
|
|
6
7
|
from vellum.client.types.function_definition import FunctionDefinition
|
8
|
+
from vellum.client.types.string_vellum_value import StringVellumValue
|
9
|
+
from vellum.client.types.vellum_variable import VellumVariable
|
7
10
|
from vellum.workflows import BaseWorkflow
|
8
11
|
from vellum.workflows.inputs.base import BaseInputs
|
9
12
|
from vellum.workflows.nodes.bases.base import BaseNode
|
10
13
|
from vellum.workflows.state.base import BaseState
|
11
|
-
from vellum.workflows.utils.functions import
|
14
|
+
from vellum.workflows.utils.functions import (
|
15
|
+
compile_deployment_workflow_function_definition,
|
16
|
+
compile_function_definition,
|
17
|
+
compile_workflow_function_definition,
|
18
|
+
)
|
12
19
|
|
13
20
|
|
14
21
|
def test_compile_function_definition__just_name():
|
@@ -431,3 +438,146 @@ def test_compile_workflow_function_definition__optionals():
|
|
431
438
|
"required": ["a", "b", "c"],
|
432
439
|
},
|
433
440
|
)
|
441
|
+
|
442
|
+
|
443
|
+
def test_compile_deployment_workflow_function_definition__just_name():
|
444
|
+
# GIVEN a mock Vellum client and deployment
|
445
|
+
mock_client = Mock()
|
446
|
+
mock_release = Mock()
|
447
|
+
mock_release.workflow_version.input_variables = []
|
448
|
+
mock_release.description = "This is a test deployment"
|
449
|
+
mock_client.release_reviews.retrieve_workflow_deployment_release.return_value = mock_release
|
450
|
+
|
451
|
+
deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
|
452
|
+
|
453
|
+
# WHEN compiling the deployment workflow function
|
454
|
+
compiled_function = compile_deployment_workflow_function_definition(deployment_config, mock_client)
|
455
|
+
|
456
|
+
# THEN it should return the compiled function definition (same structure as function test)
|
457
|
+
assert compiled_function == FunctionDefinition(
|
458
|
+
name="my_deployment",
|
459
|
+
description="This is a test deployment",
|
460
|
+
parameters={"type": "object", "properties": {}, "required": []},
|
461
|
+
)
|
462
|
+
|
463
|
+
|
464
|
+
def test_compile_deployment_workflow_function_definition__all_args():
|
465
|
+
# GIVEN a mock Vellum client and deployment
|
466
|
+
mock_client = Mock()
|
467
|
+
mock_release = Mock()
|
468
|
+
|
469
|
+
mock_inputs = []
|
470
|
+
for key, vellum_type in [
|
471
|
+
("a", "STRING"),
|
472
|
+
("b", "NUMBER"),
|
473
|
+
("c", "JSON"),
|
474
|
+
("d", "CHAT_HISTORY"),
|
475
|
+
("e", "SEARCH_RESULTS"),
|
476
|
+
("f", "ERROR"),
|
477
|
+
("g", "ARRAY"),
|
478
|
+
("h", "FUNCTION_CALL"),
|
479
|
+
("i", "IMAGE"),
|
480
|
+
("j", "AUDIO"),
|
481
|
+
("k", "DOCUMENT"),
|
482
|
+
("l", "NULL"),
|
483
|
+
]:
|
484
|
+
mock_input = VellumVariable(
|
485
|
+
id=f"input_{key}",
|
486
|
+
key=key,
|
487
|
+
type=vellum_type,
|
488
|
+
required=True,
|
489
|
+
default=None,
|
490
|
+
)
|
491
|
+
mock_inputs.append(mock_input)
|
492
|
+
|
493
|
+
mock_release.workflow_version.input_variables = mock_inputs
|
494
|
+
mock_release.description = "This is a test deployment"
|
495
|
+
mock_client.release_reviews.retrieve_workflow_deployment_release.return_value = mock_release
|
496
|
+
|
497
|
+
deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
|
498
|
+
|
499
|
+
# WHEN compiling the deployment workflow function
|
500
|
+
compiled_function = compile_deployment_workflow_function_definition(deployment_config, mock_client)
|
501
|
+
|
502
|
+
# THEN it should return the compiled function definition
|
503
|
+
assert compiled_function == FunctionDefinition(
|
504
|
+
name="my_deployment",
|
505
|
+
description="This is a test deployment",
|
506
|
+
parameters={
|
507
|
+
"type": "object",
|
508
|
+
"properties": {
|
509
|
+
"a": {"type": "string"},
|
510
|
+
"b": {"type": "number"},
|
511
|
+
"c": {"type": "object"},
|
512
|
+
"d": {"type": "array"},
|
513
|
+
"e": {"type": "array"},
|
514
|
+
"f": {"type": "object"},
|
515
|
+
"g": {"type": "array"},
|
516
|
+
"h": {"type": "object"},
|
517
|
+
"i": {"type": "object"},
|
518
|
+
"j": {"type": "object"},
|
519
|
+
"k": {"type": "object"},
|
520
|
+
"l": {"type": "null"},
|
521
|
+
},
|
522
|
+
"required": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
|
523
|
+
},
|
524
|
+
)
|
525
|
+
|
526
|
+
|
527
|
+
def test_compile_deployment_workflow_function_definition__defaults():
|
528
|
+
# GIVEN a mock Vellum client and deployment
|
529
|
+
mock_client = Mock()
|
530
|
+
mock_release = Mock()
|
531
|
+
|
532
|
+
mock_inputs = []
|
533
|
+
|
534
|
+
mock_input_no_default = VellumVariable(
|
535
|
+
id="no_default",
|
536
|
+
key="no_default",
|
537
|
+
type="STRING",
|
538
|
+
required=True,
|
539
|
+
default=None,
|
540
|
+
)
|
541
|
+
mock_inputs.append(mock_input_no_default)
|
542
|
+
|
543
|
+
mock_input_null_default = VellumVariable(
|
544
|
+
id="null_default",
|
545
|
+
key="null_default",
|
546
|
+
type="STRING",
|
547
|
+
required=False,
|
548
|
+
default=StringVellumValue(value=None),
|
549
|
+
)
|
550
|
+
mock_inputs.append(mock_input_null_default)
|
551
|
+
|
552
|
+
mock_input_actual_default = VellumVariable(
|
553
|
+
id="actual_default",
|
554
|
+
key="actual_default",
|
555
|
+
type="STRING",
|
556
|
+
required=False,
|
557
|
+
default=StringVellumValue(value="hello world"),
|
558
|
+
)
|
559
|
+
mock_inputs.append(mock_input_actual_default)
|
560
|
+
|
561
|
+
mock_release.workflow_version.input_variables = mock_inputs
|
562
|
+
mock_release.description = "This is a test deployment"
|
563
|
+
mock_client.release_reviews.retrieve_workflow_deployment_release.return_value = mock_release
|
564
|
+
|
565
|
+
deployment_config = {"deployment": "my_deployment", "release_tag": "latest"}
|
566
|
+
|
567
|
+
# WHEN compiling the deployment workflow function
|
568
|
+
compiled_function = compile_deployment_workflow_function_definition(deployment_config, mock_client)
|
569
|
+
|
570
|
+
# THEN it should return the compiled function definition with proper default handling
|
571
|
+
assert compiled_function == FunctionDefinition(
|
572
|
+
name="my_deployment",
|
573
|
+
description="This is a test deployment",
|
574
|
+
parameters={
|
575
|
+
"type": "object",
|
576
|
+
"properties": {
|
577
|
+
"no_default": {"type": "string"},
|
578
|
+
"null_default": {"type": "string", "default": None},
|
579
|
+
"actual_default": {"type": "string", "default": "hello world"},
|
580
|
+
},
|
581
|
+
"required": ["no_default"],
|
582
|
+
},
|
583
|
+
)
|
@@ -3,7 +3,10 @@ from typing import List, Optional
|
|
3
3
|
|
4
4
|
from vellum import ChatMessage, SearchResult, VellumAudio, VellumDocument, VellumImage
|
5
5
|
from vellum.workflows.types.core import Json
|
6
|
-
from vellum.workflows.utils.vellum_variables import
|
6
|
+
from vellum.workflows.utils.vellum_variables import (
|
7
|
+
primitive_type_to_vellum_variable_type,
|
8
|
+
vellum_variable_type_to_openapi_type,
|
9
|
+
)
|
7
10
|
|
8
11
|
|
9
12
|
@pytest.mark.parametrize(
|
@@ -31,3 +34,24 @@ from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_var
|
|
31
34
|
)
|
32
35
|
def test_primitive_type_to_vellum_variable_type(type_, expected):
|
33
36
|
assert primitive_type_to_vellum_variable_type(type_) == expected
|
37
|
+
|
38
|
+
|
39
|
+
@pytest.mark.parametrize(
|
40
|
+
"vellum_type, expected",
|
41
|
+
[
|
42
|
+
("STRING", "string"),
|
43
|
+
("NUMBER", "number"),
|
44
|
+
("JSON", "object"),
|
45
|
+
("CHAT_HISTORY", "array"),
|
46
|
+
("SEARCH_RESULTS", "array"),
|
47
|
+
("ERROR", "object"),
|
48
|
+
("ARRAY", "array"),
|
49
|
+
("FUNCTION_CALL", "object"),
|
50
|
+
("IMAGE", "object"),
|
51
|
+
("AUDIO", "object"),
|
52
|
+
("DOCUMENT", "object"),
|
53
|
+
("NULL", "null"),
|
54
|
+
],
|
55
|
+
)
|
56
|
+
def test_vellum_variable_type_to_openapi_type(vellum_type, expected):
|
57
|
+
assert vellum_variable_type_to_openapi_type(vellum_type) == expected
|
@@ -78,6 +78,36 @@ def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -
|
|
78
78
|
return "JSON"
|
79
79
|
|
80
80
|
|
81
|
+
def vellum_variable_type_to_openapi_type(vellum_type: VellumVariableType) -> str:
|
82
|
+
"""Converts a VellumVariableType to a JSON schema primitive type string"""
|
83
|
+
if vellum_type == "STRING":
|
84
|
+
return "string"
|
85
|
+
elif vellum_type == "NUMBER":
|
86
|
+
return "number"
|
87
|
+
elif vellum_type == "JSON":
|
88
|
+
return "object"
|
89
|
+
elif vellum_type == "CHAT_HISTORY":
|
90
|
+
return "array"
|
91
|
+
elif vellum_type == "SEARCH_RESULTS":
|
92
|
+
return "array"
|
93
|
+
elif vellum_type == "ERROR":
|
94
|
+
return "object"
|
95
|
+
elif vellum_type == "ARRAY":
|
96
|
+
return "array"
|
97
|
+
elif vellum_type == "FUNCTION_CALL":
|
98
|
+
return "object"
|
99
|
+
elif vellum_type == "IMAGE":
|
100
|
+
return "object"
|
101
|
+
elif vellum_type == "AUDIO":
|
102
|
+
return "object"
|
103
|
+
elif vellum_type == "DOCUMENT":
|
104
|
+
return "object"
|
105
|
+
elif vellum_type == "NULL":
|
106
|
+
return "null"
|
107
|
+
else:
|
108
|
+
return "object"
|
109
|
+
|
110
|
+
|
81
111
|
def _is_type_optionally_equal(type_: Type, target_type: Type) -> bool:
|
82
112
|
if type_ == target_type:
|
83
113
|
return True
|
@@ -42,6 +42,7 @@ from vellum.workflows.events.node import (
|
|
42
42
|
NodeExecutionStreamingBody,
|
43
43
|
NodeExecutionStreamingEvent,
|
44
44
|
)
|
45
|
+
from vellum.workflows.events.stream import WorkflowEventGenerator
|
45
46
|
from vellum.workflows.events.workflow import (
|
46
47
|
GenericWorkflowEvent,
|
47
48
|
WorkflowExecutionFulfilledBody,
|
@@ -167,7 +168,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
167
168
|
WorkflowExecutionPausedEvent,
|
168
169
|
]
|
169
170
|
|
170
|
-
WorkflowEventStream =
|
171
|
+
WorkflowEventStream = WorkflowEventGenerator[WorkflowEvent]
|
171
172
|
|
172
173
|
def __init__(
|
173
174
|
self,
|
@@ -444,7 +445,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
444
445
|
"""
|
445
446
|
|
446
447
|
should_yield = event_filter or workflow_event_filter
|
447
|
-
|
448
|
+
runner_stream = WorkflowRunner(
|
448
449
|
self,
|
449
450
|
inputs=inputs,
|
450
451
|
state=state,
|
@@ -454,9 +455,14 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
454
455
|
node_output_mocks=node_output_mocks,
|
455
456
|
max_concurrency=max_concurrency,
|
456
457
|
init_execution_context=self._execution_context,
|
457
|
-
).stream()
|
458
|
-
|
459
|
-
|
458
|
+
).stream()
|
459
|
+
|
460
|
+
def _generate_filtered_events() -> Generator[BaseWorkflow.WorkflowEvent, None, None]:
|
461
|
+
for event in runner_stream:
|
462
|
+
if should_yield(self.__class__, event):
|
463
|
+
yield event
|
464
|
+
|
465
|
+
return WorkflowEventGenerator(_generate_filtered_events(), runner_stream.span_id)
|
460
466
|
|
461
467
|
def validate(self) -> None:
|
462
468
|
"""
|