vellum-ai 0.12.4__py3-none-any.whl → 0.12.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/resources/workflows/client.py +32 -0
- vellum/client/types/chat_message_prompt_block.py +1 -1
- vellum/client/types/function_definition.py +26 -7
- vellum/client/types/jinja_prompt_block.py +1 -1
- vellum/client/types/plain_text_prompt_block.py +1 -1
- vellum/client/types/rich_text_prompt_block.py +1 -1
- vellum/client/types/variable_prompt_block.py +1 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +13 -4
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +55 -0
- vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +1 -1
- vellum/workflows/sandbox.py +51 -0
- vellum/workflows/tests/__init__.py +0 -0
- vellum/workflows/tests/test_sandbox.py +62 -0
- vellum/workflows/utils/functions.py +41 -4
- vellum/workflows/utils/tests/test_functions.py +93 -0
- {vellum_ai-0.12.4.dist-info → vellum_ai-0.12.6.dist-info}/METADATA +1 -1
- {vellum_ai-0.12.4.dist-info → vellum_ai-0.12.6.dist-info}/RECORD +30 -27
- vellum_cli/__init__.py +14 -0
- vellum_cli/pull.py +16 -2
- vellum_cli/tests/test_pull.py +45 -0
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -3
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py +18 -18
- vellum_ee/workflows/display/nodes/vellum/search_node.py +19 -15
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/utils.py +4 -4
- {vellum_ai-0.12.4.dist-info → vellum_ai-0.12.6.dist-info}/LICENSE +0 -0
- {vellum_ai-0.12.4.dist-info → vellum_ai-0.12.6.dist-info}/WHEEL +0 -0
- {vellum_ai-0.12.4.dist-info → vellum_ai-0.12.6.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.12.
|
21
|
+
"X-Fern-SDK-Version": "0.12.6",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -27,7 +27,11 @@ class WorkflowsClient:
|
|
27
27
|
self,
|
28
28
|
id: str,
|
29
29
|
*,
|
30
|
+
exclude_code: typing.Optional[bool] = None,
|
30
31
|
format: typing.Optional[WorkflowsPullRequestFormat] = None,
|
32
|
+
include_json: typing.Optional[bool] = None,
|
33
|
+
include_sandbox: typing.Optional[bool] = None,
|
34
|
+
strict: typing.Optional[bool] = None,
|
31
35
|
request_options: typing.Optional[RequestOptions] = None,
|
32
36
|
) -> typing.Iterator[bytes]:
|
33
37
|
"""
|
@@ -38,8 +42,16 @@ class WorkflowsClient:
|
|
38
42
|
id : str
|
39
43
|
The ID of the Workflow to pull from
|
40
44
|
|
45
|
+
exclude_code : typing.Optional[bool]
|
46
|
+
|
41
47
|
format : typing.Optional[WorkflowsPullRequestFormat]
|
42
48
|
|
49
|
+
include_json : typing.Optional[bool]
|
50
|
+
|
51
|
+
include_sandbox : typing.Optional[bool]
|
52
|
+
|
53
|
+
strict : typing.Optional[bool]
|
54
|
+
|
43
55
|
request_options : typing.Optional[RequestOptions]
|
44
56
|
Request-specific configuration.
|
45
57
|
|
@@ -53,7 +65,11 @@ class WorkflowsClient:
|
|
53
65
|
base_url=self._client_wrapper.get_environment().default,
|
54
66
|
method="GET",
|
55
67
|
params={
|
68
|
+
"exclude_code": exclude_code,
|
56
69
|
"format": format,
|
70
|
+
"include_json": include_json,
|
71
|
+
"include_sandbox": include_sandbox,
|
72
|
+
"strict": strict,
|
57
73
|
},
|
58
74
|
request_options=request_options,
|
59
75
|
) as _response:
|
@@ -164,7 +180,11 @@ class AsyncWorkflowsClient:
|
|
164
180
|
self,
|
165
181
|
id: str,
|
166
182
|
*,
|
183
|
+
exclude_code: typing.Optional[bool] = None,
|
167
184
|
format: typing.Optional[WorkflowsPullRequestFormat] = None,
|
185
|
+
include_json: typing.Optional[bool] = None,
|
186
|
+
include_sandbox: typing.Optional[bool] = None,
|
187
|
+
strict: typing.Optional[bool] = None,
|
168
188
|
request_options: typing.Optional[RequestOptions] = None,
|
169
189
|
) -> typing.AsyncIterator[bytes]:
|
170
190
|
"""
|
@@ -175,8 +195,16 @@ class AsyncWorkflowsClient:
|
|
175
195
|
id : str
|
176
196
|
The ID of the Workflow to pull from
|
177
197
|
|
198
|
+
exclude_code : typing.Optional[bool]
|
199
|
+
|
178
200
|
format : typing.Optional[WorkflowsPullRequestFormat]
|
179
201
|
|
202
|
+
include_json : typing.Optional[bool]
|
203
|
+
|
204
|
+
include_sandbox : typing.Optional[bool]
|
205
|
+
|
206
|
+
strict : typing.Optional[bool]
|
207
|
+
|
180
208
|
request_options : typing.Optional[RequestOptions]
|
181
209
|
Request-specific configuration.
|
182
210
|
|
@@ -190,7 +218,11 @@ class AsyncWorkflowsClient:
|
|
190
218
|
base_url=self._client_wrapper.get_environment().default,
|
191
219
|
method="GET",
|
192
220
|
params={
|
221
|
+
"exclude_code": exclude_code,
|
193
222
|
"format": format,
|
223
|
+
"include_json": include_json,
|
224
|
+
"include_sandbox": include_sandbox,
|
225
|
+
"strict": strict,
|
194
226
|
},
|
195
227
|
request_options=request_options,
|
196
228
|
) as _response:
|
@@ -16,9 +16,9 @@ class ChatMessagePromptBlock(UniversalBaseModel):
|
|
16
16
|
A block that represents a chat message in a prompt template.
|
17
17
|
"""
|
18
18
|
|
19
|
+
block_type: typing.Literal["CHAT_MESSAGE"] = "CHAT_MESSAGE"
|
19
20
|
state: typing.Optional[PromptBlockState] = None
|
20
21
|
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
21
|
-
block_type: typing.Literal["CHAT_MESSAGE"] = "CHAT_MESSAGE"
|
22
22
|
chat_role: ChatMessageRole
|
23
23
|
chat_source: typing.Optional[str] = None
|
24
24
|
chat_message_unterminated: typing.Optional[bool] = None
|
@@ -4,22 +4,41 @@ from ..core.pydantic_utilities import UniversalBaseModel
|
|
4
4
|
import typing
|
5
5
|
from .prompt_block_state import PromptBlockState
|
6
6
|
from .ephemeral_prompt_cache_config import EphemeralPromptCacheConfig
|
7
|
-
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
8
7
|
import pydantic
|
8
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
9
9
|
|
10
10
|
|
11
11
|
class FunctionDefinition(UniversalBaseModel):
|
12
12
|
"""
|
13
|
-
|
13
|
+
The definition of a Function (aka "Tool Call") that a Prompt/Model has access to.
|
14
14
|
"""
|
15
15
|
|
16
16
|
state: typing.Optional[PromptBlockState] = None
|
17
17
|
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
18
|
-
name: typing.Optional[str] = None
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
name: typing.Optional[str] = pydantic.Field(default=None)
|
19
|
+
"""
|
20
|
+
The name identifying the function.
|
21
|
+
"""
|
22
|
+
|
23
|
+
description: typing.Optional[str] = pydantic.Field(default=None)
|
24
|
+
"""
|
25
|
+
A description to help guide the model when to invoke this function.
|
26
|
+
"""
|
27
|
+
|
28
|
+
parameters: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None)
|
29
|
+
"""
|
30
|
+
An OpenAPI specification of parameters that are supported by this function.
|
31
|
+
"""
|
32
|
+
|
33
|
+
forced: typing.Optional[bool] = pydantic.Field(default=None)
|
34
|
+
"""
|
35
|
+
Set this option to true to force the model to return a function call of this function.
|
36
|
+
"""
|
37
|
+
|
38
|
+
strict: typing.Optional[bool] = pydantic.Field(default=None)
|
39
|
+
"""
|
40
|
+
Set this option to use strict schema decoding when available.
|
41
|
+
"""
|
23
42
|
|
24
43
|
if IS_PYDANTIC_V2:
|
25
44
|
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
@@ -13,9 +13,9 @@ class JinjaPromptBlock(UniversalBaseModel):
|
|
13
13
|
A block of Jinja template code that is used to generate a prompt
|
14
14
|
"""
|
15
15
|
|
16
|
+
block_type: typing.Literal["JINJA"] = "JINJA"
|
16
17
|
state: typing.Optional[PromptBlockState] = None
|
17
18
|
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
18
|
-
block_type: typing.Literal["JINJA"] = "JINJA"
|
19
19
|
template: str
|
20
20
|
|
21
21
|
if IS_PYDANTIC_V2:
|
@@ -13,9 +13,9 @@ class PlainTextPromptBlock(UniversalBaseModel):
|
|
13
13
|
A block that holds a plain text string value.
|
14
14
|
"""
|
15
15
|
|
16
|
+
block_type: typing.Literal["PLAIN_TEXT"] = "PLAIN_TEXT"
|
16
17
|
state: typing.Optional[PromptBlockState] = None
|
17
18
|
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
18
|
-
block_type: typing.Literal["PLAIN_TEXT"] = "PLAIN_TEXT"
|
19
19
|
text: str
|
20
20
|
|
21
21
|
if IS_PYDANTIC_V2:
|
@@ -14,9 +14,9 @@ class RichTextPromptBlock(UniversalBaseModel):
|
|
14
14
|
A block that includes a combination of plain text and variable blocks.
|
15
15
|
"""
|
16
16
|
|
17
|
+
block_type: typing.Literal["RICH_TEXT"] = "RICH_TEXT"
|
17
18
|
state: typing.Optional[PromptBlockState] = None
|
18
19
|
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
19
|
-
block_type: typing.Literal["RICH_TEXT"] = "RICH_TEXT"
|
20
20
|
blocks: typing.List[RichTextChildBlock]
|
21
21
|
|
22
22
|
if IS_PYDANTIC_V2:
|
@@ -13,9 +13,9 @@ class VariablePromptBlock(UniversalBaseModel):
|
|
13
13
|
A block that represents a variable in a prompt template.
|
14
14
|
"""
|
15
15
|
|
16
|
+
block_type: typing.Literal["VARIABLE"] = "VARIABLE"
|
16
17
|
state: typing.Optional[PromptBlockState] = None
|
17
18
|
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
18
|
-
block_type: typing.Literal["VARIABLE"] = "VARIABLE"
|
19
19
|
input_variable: str
|
20
20
|
|
21
21
|
if IS_PYDANTIC_V2:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
from uuid import uuid4
|
3
|
-
from typing import ClassVar, Generic, Iterator, List, Optional, Tuple, cast
|
3
|
+
from typing import Callable, ClassVar, Generic, Iterator, List, Optional, Tuple, Union, cast
|
4
4
|
|
5
5
|
from vellum import (
|
6
6
|
AdHocExecutePromptEvent,
|
@@ -24,9 +24,10 @@ from vellum.workflows.exceptions import NodeException
|
|
24
24
|
from vellum.workflows.nodes.displayable.bases.base_prompt_node import BasePromptNode
|
25
25
|
from vellum.workflows.nodes.displayable.bases.inline_prompt_node.constants import DEFAULT_PROMPT_PARAMETERS
|
26
26
|
from vellum.workflows.types.generics import StateType
|
27
|
+
from vellum.workflows.utils.functions import compile_function_definition
|
27
28
|
|
28
29
|
|
29
|
-
class BaseInlinePromptNode(BasePromptNode, Generic[StateType]):
|
30
|
+
class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
|
30
31
|
"""
|
31
32
|
Used to execute a Prompt defined inline.
|
32
33
|
|
@@ -45,7 +46,7 @@ class BaseInlinePromptNode(BasePromptNode, Generic[StateType]):
|
|
45
46
|
blocks: ClassVar[List[PromptBlock]]
|
46
47
|
|
47
48
|
# The functions/tools that a Prompt has access to
|
48
|
-
functions: Optional[List[FunctionDefinition]] =
|
49
|
+
functions: Optional[List[Union[FunctionDefinition, Callable]]] = None
|
49
50
|
|
50
51
|
parameters: PromptParameters = DEFAULT_PROMPT_PARAMETERS
|
51
52
|
expand_meta: Optional[AdHocExpandMeta] = OMIT
|
@@ -59,6 +60,14 @@ class BaseInlinePromptNode(BasePromptNode, Generic[StateType]):
|
|
59
60
|
"execution_context": {"parent_context": parent_context},
|
60
61
|
**request_options.get("additional_body_parameters", {}),
|
61
62
|
}
|
63
|
+
normalized_functions = (
|
64
|
+
[
|
65
|
+
function if isinstance(function, FunctionDefinition) else compile_function_definition(function)
|
66
|
+
for function in self.functions
|
67
|
+
]
|
68
|
+
if self.functions
|
69
|
+
else None
|
70
|
+
)
|
62
71
|
|
63
72
|
return self._context.vellum_client.ad_hoc.adhoc_execute_prompt_stream(
|
64
73
|
ml_model=self.ml_model,
|
@@ -66,7 +75,7 @@ class BaseInlinePromptNode(BasePromptNode, Generic[StateType]):
|
|
66
75
|
input_variables=input_variables,
|
67
76
|
parameters=self.parameters,
|
68
77
|
blocks=self.blocks,
|
69
|
-
functions=
|
78
|
+
functions=normalized_functions,
|
70
79
|
expand_meta=self.expand_meta,
|
71
80
|
request_options=self.request_options,
|
72
81
|
)
|
@@ -5,10 +5,14 @@ from typing import Any, Iterator, List
|
|
5
5
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
6
6
|
from vellum.client.types.execute_prompt_event import ExecutePromptEvent
|
7
7
|
from vellum.client.types.fulfilled_execute_prompt_event import FulfilledExecutePromptEvent
|
8
|
+
from vellum.client.types.function_call import FunctionCall
|
9
|
+
from vellum.client.types.function_call_vellum_value import FunctionCallVellumValue
|
10
|
+
from vellum.client.types.function_definition import FunctionDefinition
|
8
11
|
from vellum.client.types.initiated_execute_prompt_event import InitiatedExecutePromptEvent
|
9
12
|
from vellum.client.types.prompt_output import PromptOutput
|
10
13
|
from vellum.client.types.prompt_request_json_input import PromptRequestJsonInput
|
11
14
|
from vellum.client.types.string_vellum_value import StringVellumValue
|
15
|
+
from vellum.workflows.nodes.displayable.bases.inline_prompt_node.node import BaseInlinePromptNode
|
12
16
|
from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
|
13
17
|
|
14
18
|
|
@@ -62,3 +66,54 @@ def test_inline_prompt_node__json_inputs(vellum_adhoc_prompt_client):
|
|
62
66
|
PromptRequestJsonInput(key="a_pydantic", type="JSON", value={"example": "example"}),
|
63
67
|
]
|
64
68
|
assert len(mock_api.call_args.kwargs["input_variables"]) == 4
|
69
|
+
|
70
|
+
|
71
|
+
def test_inline_prompt_node__function_definitions(vellum_adhoc_prompt_client):
|
72
|
+
# GIVEN a function definition
|
73
|
+
def my_function(foo: str, bar: int) -> None:
|
74
|
+
pass
|
75
|
+
|
76
|
+
# AND a prompt node with a accepting that function definition
|
77
|
+
class MyNode(BaseInlinePromptNode):
|
78
|
+
ml_model = "gpt-4o"
|
79
|
+
functions = [my_function]
|
80
|
+
prompt_inputs = {}
|
81
|
+
blocks = []
|
82
|
+
|
83
|
+
# AND a known response from invoking an inline prompt
|
84
|
+
expected_outputs: List[PromptOutput] = [
|
85
|
+
FunctionCallVellumValue(value=FunctionCall(name="my_function", arguments={"foo": "hello", "bar": 1})),
|
86
|
+
]
|
87
|
+
|
88
|
+
def generate_prompt_events(*args: Any, **kwargs: Any) -> Iterator[ExecutePromptEvent]:
|
89
|
+
execution_id = str(uuid4())
|
90
|
+
events: List[ExecutePromptEvent] = [
|
91
|
+
InitiatedExecutePromptEvent(execution_id=execution_id),
|
92
|
+
FulfilledExecutePromptEvent(
|
93
|
+
execution_id=execution_id,
|
94
|
+
outputs=expected_outputs,
|
95
|
+
),
|
96
|
+
]
|
97
|
+
yield from events
|
98
|
+
|
99
|
+
vellum_adhoc_prompt_client.adhoc_execute_prompt_stream.side_effect = generate_prompt_events
|
100
|
+
|
101
|
+
# WHEN the node is run
|
102
|
+
list(MyNode().run())
|
103
|
+
|
104
|
+
# THEN the prompt is executed with the correct inputs
|
105
|
+
mock_api = vellum_adhoc_prompt_client.adhoc_execute_prompt_stream
|
106
|
+
assert mock_api.call_count == 1
|
107
|
+
assert mock_api.call_args.kwargs["functions"] == [
|
108
|
+
FunctionDefinition(
|
109
|
+
name="my_function",
|
110
|
+
parameters={
|
111
|
+
"type": "object",
|
112
|
+
"properties": {
|
113
|
+
"foo": {"type": "string"},
|
114
|
+
"bar": {"type": "integer"},
|
115
|
+
},
|
116
|
+
"required": ["foo", "bar"],
|
117
|
+
},
|
118
|
+
),
|
119
|
+
]
|
@@ -75,7 +75,7 @@ def test_inline_text_prompt_node__basic(vellum_adhoc_prompt_client):
|
|
75
75
|
vellum_adhoc_prompt_client.adhoc_execute_prompt_stream.assert_called_once_with(
|
76
76
|
blocks=[],
|
77
77
|
expand_meta=Ellipsis,
|
78
|
-
functions=
|
78
|
+
functions=None,
|
79
79
|
input_values=[],
|
80
80
|
input_variables=[],
|
81
81
|
ml_model="gpt-4o",
|
@@ -0,0 +1,51 @@
|
|
1
|
+
from typing import Generic, Sequence, Type
|
2
|
+
|
3
|
+
import dotenv
|
4
|
+
|
5
|
+
from vellum.workflows.events.workflow import WorkflowEventStream
|
6
|
+
from vellum.workflows.inputs.base import BaseInputs
|
7
|
+
from vellum.workflows.logging import load_logger
|
8
|
+
from vellum.workflows.types.generics import WorkflowType
|
9
|
+
from vellum.workflows.workflows.event_filters import root_workflow_event_filter
|
10
|
+
|
11
|
+
|
12
|
+
class SandboxRunner(Generic[WorkflowType]):
|
13
|
+
def __init__(self, workflow: Type[WorkflowType], inputs: Sequence[BaseInputs]):
|
14
|
+
if not inputs:
|
15
|
+
raise ValueError("Inputs are required to have at least one defined inputs")
|
16
|
+
|
17
|
+
self._workflow = workflow
|
18
|
+
self._inputs = inputs
|
19
|
+
|
20
|
+
dotenv.load_dotenv()
|
21
|
+
self._logger = load_logger()
|
22
|
+
|
23
|
+
def run(self, index: int = 0):
|
24
|
+
if index < 0:
|
25
|
+
self._logger.warning("Index is less than 0, running first input")
|
26
|
+
index = 0
|
27
|
+
elif index >= len(self._inputs):
|
28
|
+
self._logger.warning("Index is greater than the number of provided inputs, running last input")
|
29
|
+
index = len(self._inputs) - 1
|
30
|
+
|
31
|
+
selected_inputs = self._inputs[index]
|
32
|
+
|
33
|
+
workflow = self._workflow()
|
34
|
+
events = workflow.stream(
|
35
|
+
inputs=selected_inputs,
|
36
|
+
event_filter=root_workflow_event_filter,
|
37
|
+
)
|
38
|
+
|
39
|
+
self._process_events(events)
|
40
|
+
|
41
|
+
def _process_events(self, events: WorkflowEventStream):
|
42
|
+
for event in events:
|
43
|
+
if event.name == "workflow.execution.fulfilled":
|
44
|
+
self._logger.info("Workflow fulfilled!")
|
45
|
+
for output_reference, value in event.outputs:
|
46
|
+
self._logger.info("----------------------------------")
|
47
|
+
self._logger.info(f"{output_reference.name}: {value}")
|
48
|
+
elif event.name == "node.execution.initiated":
|
49
|
+
self._logger.info(f"Just started Node: {event.node_definition.__name__}")
|
50
|
+
elif event.name == "node.execution.fulfilled":
|
51
|
+
self._logger.info(f"Just finished Node: {event.node_definition.__name__}")
|
File without changes
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import pytest
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
from vellum.workflows.inputs.base import BaseInputs
|
5
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
6
|
+
from vellum.workflows.sandbox import SandboxRunner
|
7
|
+
from vellum.workflows.state.base import BaseState
|
8
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
9
|
+
|
10
|
+
|
11
|
+
@pytest.fixture
|
12
|
+
def mock_logger(mocker):
|
13
|
+
return mocker.patch("vellum.workflows.sandbox.load_logger")
|
14
|
+
|
15
|
+
|
16
|
+
@pytest.mark.parametrize(
|
17
|
+
["run_kwargs", "expected_last_log"],
|
18
|
+
[
|
19
|
+
({}, "final_results: first"),
|
20
|
+
({"index": 1}, "final_results: second"),
|
21
|
+
({"index": -4}, "final_results: first"),
|
22
|
+
({"index": 100}, "final_results: second"),
|
23
|
+
],
|
24
|
+
ids=["default", "specific", "negative", "out_of_bounds"],
|
25
|
+
)
|
26
|
+
def test_sandbox_runner__happy_path(mock_logger, run_kwargs, expected_last_log):
|
27
|
+
# GIVEN we capture the logs to stdout
|
28
|
+
logs = []
|
29
|
+
mock_logger.return_value.info.side_effect = lambda msg: logs.append(msg)
|
30
|
+
|
31
|
+
# AND an example workflow
|
32
|
+
class Inputs(BaseInputs):
|
33
|
+
foo: str
|
34
|
+
|
35
|
+
class StartNode(BaseNode):
|
36
|
+
class Outputs(BaseNode.Outputs):
|
37
|
+
bar = Inputs.foo
|
38
|
+
|
39
|
+
class Workflow(BaseWorkflow[Inputs, BaseState]):
|
40
|
+
graph = StartNode
|
41
|
+
|
42
|
+
class Outputs(BaseWorkflow.Outputs):
|
43
|
+
final_results = StartNode.Outputs.bar
|
44
|
+
|
45
|
+
# AND a dataset for this workflow
|
46
|
+
inputs: List[Inputs] = [
|
47
|
+
Inputs(foo="first"),
|
48
|
+
Inputs(foo="second"),
|
49
|
+
]
|
50
|
+
|
51
|
+
# WHEN we run the sandbox
|
52
|
+
runner = SandboxRunner(workflow=Workflow, inputs=inputs)
|
53
|
+
runner.run(**run_kwargs)
|
54
|
+
|
55
|
+
# THEN we see the logs
|
56
|
+
assert logs == [
|
57
|
+
"Just started Node: StartNode",
|
58
|
+
"Just finished Node: StartNode",
|
59
|
+
"Workflow fulfilled!",
|
60
|
+
"----------------------------------",
|
61
|
+
expected_last_log,
|
62
|
+
]
|
@@ -1,6 +1,9 @@
|
|
1
1
|
import dataclasses
|
2
2
|
import inspect
|
3
|
-
from typing import Any, Callable, Union, get_args, get_origin
|
3
|
+
from typing import Any, Callable, Optional, Union, get_args, get_origin
|
4
|
+
|
5
|
+
from pydantic import BaseModel
|
6
|
+
from pydantic_core import PydanticUndefined
|
4
7
|
|
5
8
|
from vellum.client.types.function_definition import FunctionDefinition
|
6
9
|
|
@@ -16,7 +19,10 @@ type_map = {
|
|
16
19
|
}
|
17
20
|
|
18
21
|
|
19
|
-
def _compile_annotation(annotation: Any, defs: dict[str, Any]) -> dict:
|
22
|
+
def _compile_annotation(annotation: Optional[Any], defs: dict[str, Any]) -> dict:
|
23
|
+
if annotation is None:
|
24
|
+
return {"type": "null"}
|
25
|
+
|
20
26
|
if get_origin(annotation) is Union:
|
21
27
|
return {"anyOf": [_compile_annotation(a, defs) for a in get_args(annotation)]}
|
22
28
|
|
@@ -37,13 +43,44 @@ def _compile_annotation(annotation: Any, defs: dict[str, Any]) -> dict:
|
|
37
43
|
if field.default is dataclasses.MISSING:
|
38
44
|
required.append(field.name)
|
39
45
|
else:
|
40
|
-
properties[field.name]["default"] = field.default
|
46
|
+
properties[field.name]["default"] = _compile_default_value(field.default)
|
47
|
+
defs[annotation.__name__] = {"type": "object", "properties": properties, "required": required}
|
48
|
+
return {"$ref": f"#/$defs/{annotation.__name__}"}
|
49
|
+
|
50
|
+
if issubclass(annotation, BaseModel):
|
51
|
+
if annotation.__name__ not in defs:
|
52
|
+
properties = {}
|
53
|
+
required = []
|
54
|
+
for field_name, field in annotation.model_fields.items():
|
55
|
+
# Mypy is incorrect here, the `annotation` attribute is defined on `FieldInfo`
|
56
|
+
field_annotation = field.annotation # type: ignore[attr-defined]
|
57
|
+
properties[field_name] = _compile_annotation(field_annotation, defs)
|
58
|
+
if field.default is PydanticUndefined:
|
59
|
+
required.append(field_name)
|
60
|
+
else:
|
61
|
+
properties[field_name]["default"] = _compile_default_value(field.default)
|
41
62
|
defs[annotation.__name__] = {"type": "object", "properties": properties, "required": required}
|
63
|
+
|
42
64
|
return {"$ref": f"#/$defs/{annotation.__name__}"}
|
43
65
|
|
44
66
|
return {"type": type_map[annotation]}
|
45
67
|
|
46
68
|
|
69
|
+
def _compile_default_value(default: Any) -> Any:
|
70
|
+
if dataclasses.is_dataclass(default):
|
71
|
+
return {
|
72
|
+
field.name: _compile_default_value(getattr(default, field.name)) for field in dataclasses.fields(default)
|
73
|
+
}
|
74
|
+
|
75
|
+
if isinstance(default, BaseModel):
|
76
|
+
return {
|
77
|
+
field_name: _compile_default_value(getattr(default, field_name))
|
78
|
+
for field_name in default.model_fields.keys()
|
79
|
+
}
|
80
|
+
|
81
|
+
return default
|
82
|
+
|
83
|
+
|
47
84
|
def compile_function_definition(function: Callable) -> FunctionDefinition:
|
48
85
|
"""
|
49
86
|
Converts a Python function into our Vellum-native FunctionDefinition type.
|
@@ -62,7 +99,7 @@ def compile_function_definition(function: Callable) -> FunctionDefinition:
|
|
62
99
|
if param.default is inspect.Parameter.empty:
|
63
100
|
required.append(param.name)
|
64
101
|
else:
|
65
|
-
properties[param.name]["default"] = param.default
|
102
|
+
properties[param.name]["default"] = _compile_default_value(param.default)
|
66
103
|
|
67
104
|
parameters = {"type": "object", "properties": properties, "required": required}
|
68
105
|
if defs:
|
@@ -1,6 +1,8 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from typing import Dict, List, Optional, Union
|
3
3
|
|
4
|
+
from pydantic import BaseModel
|
5
|
+
|
4
6
|
from vellum.client.types.function_definition import FunctionDefinition
|
5
7
|
from vellum.workflows.utils.functions import compile_function_definition
|
6
8
|
|
@@ -169,3 +171,94 @@ def test_compile_function_definition__dataclasses():
|
|
169
171
|
},
|
170
172
|
},
|
171
173
|
)
|
174
|
+
|
175
|
+
|
176
|
+
def test_compile_function_definition__pydantic():
|
177
|
+
# GIVEN a function with a pydantic model
|
178
|
+
class MyPydanticModel(BaseModel):
|
179
|
+
a: int
|
180
|
+
b: str
|
181
|
+
|
182
|
+
def my_function(c: MyPydanticModel):
|
183
|
+
pass
|
184
|
+
|
185
|
+
# WHEN compiling the function
|
186
|
+
compiled_function = compile_function_definition(my_function)
|
187
|
+
|
188
|
+
# THEN it should return the compiled function definition
|
189
|
+
assert compiled_function == FunctionDefinition(
|
190
|
+
name="my_function",
|
191
|
+
parameters={
|
192
|
+
"type": "object",
|
193
|
+
"properties": {"c": {"$ref": "#/$defs/MyPydanticModel"}},
|
194
|
+
"required": ["c"],
|
195
|
+
"$defs": {
|
196
|
+
"MyPydanticModel": {
|
197
|
+
"type": "object",
|
198
|
+
"properties": {"a": {"type": "integer"}, "b": {"type": "string"}},
|
199
|
+
"required": ["a", "b"],
|
200
|
+
}
|
201
|
+
},
|
202
|
+
},
|
203
|
+
)
|
204
|
+
|
205
|
+
|
206
|
+
def test_compile_function_definition__default_dataclass():
|
207
|
+
# GIVEN a function with a dataclass
|
208
|
+
@dataclass
|
209
|
+
class MyDataClass:
|
210
|
+
a: int
|
211
|
+
b: str
|
212
|
+
|
213
|
+
def my_function(c: MyDataClass = MyDataClass(a=1, b="hello")):
|
214
|
+
pass
|
215
|
+
|
216
|
+
# WHEN compiling the function
|
217
|
+
compiled_function = compile_function_definition(my_function)
|
218
|
+
|
219
|
+
# THEN it should return the compiled function definition
|
220
|
+
assert compiled_function == FunctionDefinition(
|
221
|
+
name="my_function",
|
222
|
+
parameters={
|
223
|
+
"type": "object",
|
224
|
+
"properties": {"c": {"$ref": "#/$defs/MyDataClass", "default": {"a": 1, "b": "hello"}}},
|
225
|
+
"required": [],
|
226
|
+
"$defs": {
|
227
|
+
"MyDataClass": {
|
228
|
+
"type": "object",
|
229
|
+
"properties": {"a": {"type": "integer"}, "b": {"type": "string"}},
|
230
|
+
"required": ["a", "b"],
|
231
|
+
}
|
232
|
+
},
|
233
|
+
},
|
234
|
+
)
|
235
|
+
|
236
|
+
|
237
|
+
def test_compile_function_definition__default_pydantic():
|
238
|
+
# GIVEN a function with a pydantic model as the default value
|
239
|
+
class MyPydanticModel(BaseModel):
|
240
|
+
a: int
|
241
|
+
b: str
|
242
|
+
|
243
|
+
def my_function(c: MyPydanticModel = MyPydanticModel(a=1, b="hello")):
|
244
|
+
pass
|
245
|
+
|
246
|
+
# WHEN compiling the function
|
247
|
+
compiled_function = compile_function_definition(my_function)
|
248
|
+
|
249
|
+
# THEN it should return the compiled function definition
|
250
|
+
assert compiled_function == FunctionDefinition(
|
251
|
+
name="my_function",
|
252
|
+
parameters={
|
253
|
+
"type": "object",
|
254
|
+
"properties": {"c": {"$ref": "#/$defs/MyPydanticModel", "default": {"a": 1, "b": "hello"}}},
|
255
|
+
"required": [],
|
256
|
+
"$defs": {
|
257
|
+
"MyPydanticModel": {
|
258
|
+
"type": "object",
|
259
|
+
"properties": {"a": {"type": "integer"}, "b": {"type": "string"}},
|
260
|
+
"required": ["a", "b"],
|
261
|
+
}
|
262
|
+
},
|
263
|
+
},
|
264
|
+
)
|
@@ -1,17 +1,17 @@
|
|
1
1
|
vellum_cli/CONTRIBUTING.md,sha256=FtDC7BGxSeMnwCXAUssFsAIElXtmJE-O5Z7BpolcgvI,2935
|
2
2
|
vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
|
3
|
-
vellum_cli/__init__.py,sha256=
|
3
|
+
vellum_cli/__init__.py,sha256=A9uo9OE7xQACNEtX4k0c-rxypDqS5V8kA8u4BNN0azM,7402
|
4
4
|
vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
|
5
5
|
vellum_cli/config.py,sha256=wJQnv3tCgu1BOugg0AOP94yQ-x1yAg8juX_QoFN9Y7w,5223
|
6
6
|
vellum_cli/image_push.py,sha256=SJwhwWJsLjwGNezNVd_oCVpFMfPsAB3dfLWmriZZUtw,4419
|
7
7
|
vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
|
8
|
-
vellum_cli/pull.py,sha256=
|
8
|
+
vellum_cli/pull.py,sha256=q68fr1o5H9l8Dvc8BTY1GARJYjAV1i6Fg-Lg4Oo4FDw,6155
|
9
9
|
vellum_cli/push.py,sha256=gcYhIogeYejZIhNm5Cjp0VBECaXLmVQEvZjrPH0-TSU,5337
|
10
10
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
vellum_cli/tests/conftest.py,sha256=eFGwBxib3Nki830lIFintB0b6r4x8T_KMnmzhlTY5x0,1337
|
12
12
|
vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
|
13
13
|
vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
|
14
|
-
vellum_cli/tests/test_pull.py,sha256=
|
14
|
+
vellum_cli/tests/test_pull.py,sha256=P2JFNHU1hE6iydYl7rW35h7c8_DSrLiAS7gNsFMy1JU,17829
|
15
15
|
vellum_cli/tests/test_push.py,sha256=V2iGcskh2X3OHj2uV5Vx_BhmtyfmUkyx0lrp8DDOExc,5824
|
16
16
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -20,7 +20,7 @@ vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
20
20
|
vellum_ee/workflows/display/base.py,sha256=3ZFUYRNKL24fBqXhKpa_Dq2W1a-a86J20dmJYA3H2eY,1755
|
21
21
|
vellum_ee/workflows/display/nodes/__init__.py,sha256=5XOcZJXYUgaLS55QgRJzyQ_W1tpeprjnYAeYVezqoGw,160
|
22
22
|
vellum_ee/workflows/display/nodes/base_node_display.py,sha256=23PLqcpMe_mYkYdug9PDb6Br7o64Thx9-IhcviKGvGo,6662
|
23
|
-
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=
|
23
|
+
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=BxA-YVUJvB36NM-Q5DNCwckeqymwLKMp9DVCaTyrPbs,2253
|
24
24
|
vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=vyKeJAevAXvEAEtWeTEdBZXD3eJQYW_DEXLKVZ5KmYc,1325
|
25
25
|
vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
26
|
vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=QqR3Ly0RNrXwOeLdW5nERDFt0gRPf76n1bPES6o5UN4,1093
|
@@ -28,8 +28,8 @@ vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6m
|
|
28
28
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
29
29
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nmPLj8vkbVCS46XQqmHq8Xj8Mr36wCK_vWf26A9KIkw,1505
|
30
30
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=4SSQGecKWHuoGy5YIGJeOZVHGKwTs_8Y-gf3GvsHb0M,8506
|
31
|
-
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=
|
32
|
-
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=
|
31
|
+
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=XqizRn5bLwT8LMwgyvfbln8inhCxzTi1EkD22Fx-5-U,4222
|
32
|
+
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=EtdqJfhYw03PuT2iyJ6mSAZK4RsQqDie_2AnJAtMelk,13625
|
33
33
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=ygTjSjYDI4DtkxADWub5rhBnRWItMKWF6fezBrgpOKA,1979
|
34
34
|
vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=t5iJQVoRT5g-v2IiUb4kFYdvUVKch0zn27016pzDZoo,2761
|
35
35
|
vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=3TJvHX_Uuf_gr94VkYc_zmNH8I5p71ChIeoAbJZ3ddY,2158
|
@@ -39,13 +39,13 @@ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=AqUlItgSZij12qRKguKV
|
|
39
39
|
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=jzO63B9KiEAncnBqmz2ZTcxjmEHozMEe7WnfpcpsQYg,3195
|
40
40
|
vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=9VpC3h0RYOxJuRbjDwidBYlLKakkmlEnDMBh2C7lHcY,1107
|
41
41
|
vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=gLRkizwyw21-Z12IyDbdOJpXayiZZd4HWd6qgZQg8sc,3106
|
42
|
-
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=
|
42
|
+
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=laNWcDt62VRM5hLfcOi-ouAyNhYiFRsD0PWbVGLwlLI,9035
|
43
43
|
vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=zOp4voBSgB3MR1R93wTOrsiiara_hxEAYFupLl_SvTA,2657
|
44
|
-
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=
|
44
|
+
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=IHumtFFZSanRizU3-0ATFgUnDuSFZMScZce8YTDJiGU,3373
|
45
45
|
vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
46
|
vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=aJfQnIvlrRHVKgQid_gg6VQKkJyPgFnzbvWt9_t0Vz0,3860
|
47
47
|
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=hB8dcGMGkuC95kk9hmZUgHsCLwEA37fHTFXj0JzbRjM,4692
|
48
|
-
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=
|
48
|
+
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=6mPg9QilJfLw9Sgk_h9LRSyKZpKgRu06mr3X1AmmDCA,4691
|
49
49
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=TEg3QbdE7rLbEhml9pMWmay--phsekGlfGVhTblxCGE,1727
|
51
51
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -76,7 +76,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
76
76
|
vellum/client/__init__.py,sha256=o4m7iRZWEV8rP3GkdaztHAjNmjxjWERlarviFoHzuKI,110927
|
77
77
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
78
78
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
79
|
-
vellum/client/core/client_wrapper.py,sha256=
|
79
|
+
vellum/client/core/client_wrapper.py,sha256=Djd_7GxqAd6z6spaztPvuwOWM8nCLYHOM1pxOFHfmrc,1868
|
80
80
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
81
81
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
82
82
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -130,7 +130,7 @@ vellum/client/resources/workflow_deployments/types/workflow_deployments_list_req
|
|
130
130
|
vellum/client/resources/workflow_sandboxes/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
|
131
131
|
vellum/client/resources/workflow_sandboxes/client.py,sha256=3wVQxkjrJ5bIS8fB5FpKXCP2dX38299ghWrJ8YmXxwQ,7435
|
132
132
|
vellum/client/resources/workflows/__init__.py,sha256=Z4xi8Nxd9U4t35FQSepTt1p-ns0X1xtdNs168kUcuBk,153
|
133
|
-
vellum/client/resources/workflows/client.py,sha256=
|
133
|
+
vellum/client/resources/workflows/client.py,sha256=1X9KKRIr-Jwxbkd2a43DYfukKRKgNL1IXkGb9VC15a0,11391
|
134
134
|
vellum/client/resources/workflows/types/__init__.py,sha256=-uFca4ypncAOvfsg6sjD-5C9zWdA5qNvU6m675GphVg,177
|
135
135
|
vellum/client/resources/workflows/types/workflows_pull_request_format.py,sha256=dOWE_jnDnniIJLoeseeCms23aklghyBkoPmBFzcqqZk,165
|
136
136
|
vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
|
@@ -173,7 +173,7 @@ vellum/client/types/chat_history_vellum_value_request.py,sha256=HzAiysncG5unJ-tl
|
|
173
173
|
vellum/client/types/chat_message.py,sha256=EOA8v5Ebx2KS9BtwBBGbuvSK-pn4xWYZiioHuuPWvzw,916
|
174
174
|
vellum/client/types/chat_message_content.py,sha256=DQLB5rG40qLRLsmKWWo-XKa4rhk9TGQs_eFTFow2zEM,607
|
175
175
|
vellum/client/types/chat_message_content_request.py,sha256=iFT_PmN6sUjeN1_fZXr2ePJEbSq_GZcClBvtu8SdVmQ,724
|
176
|
-
vellum/client/types/chat_message_prompt_block.py,sha256=
|
176
|
+
vellum/client/types/chat_message_prompt_block.py,sha256=b6WmJqAc6r15XhHKsq3VmZBP-uoKV4jCSO-_xrDY5Qw,1351
|
177
177
|
vellum/client/types/chat_message_request.py,sha256=r2EW1pfnvNYx2fo6mBqU5HQrUzp67WXuE5G-XK281E4,945
|
178
178
|
vellum/client/types/chat_message_role.py,sha256=-i0Jrcbwf72MkMoaFTGyxRduvlN7f5Y9ULhCXR5KNdA,182
|
179
179
|
vellum/client/types/code_execution_node_array_result.py,sha256=KCdbmjXjReO-hPPpBsSR17h_roDUpc4R-92cmIn59ck,952
|
@@ -271,7 +271,7 @@ vellum/client/types/function_call_request.py,sha256=eJBIN-wLkkkDUIwAy1nMeWHu3MZ5
|
|
271
271
|
vellum/client/types/function_call_variable_value.py,sha256=VQKCiEtJsmIK3i7CtFV_2ZpxeX70rqpUViXIvAci8L0,702
|
272
272
|
vellum/client/types/function_call_vellum_value.py,sha256=lLJb-S_-S_UXm6una1BMyCbqLpMhbbMcaVIYNO45h5o,759
|
273
273
|
vellum/client/types/function_call_vellum_value_request.py,sha256=oUteuCfWcj7UJbSE_Vywmmva9kyTaeL9iv5WJHabDVs,788
|
274
|
-
vellum/client/types/function_definition.py,sha256=
|
274
|
+
vellum/client/types/function_definition.py,sha256=UzlrrBtdWe7RMStfc1UVdcnr4s8N4mOhsM306p6x_CE,1693
|
275
275
|
vellum/client/types/generate_options_request.py,sha256=TUDqsH0tiPWDZH4T-p5gsvKvwVHEVZ_k6oI3qsjlsk4,782
|
276
276
|
vellum/client/types/generate_request.py,sha256=gL6ywAJe6YCJ5oKbtYwL2H_TMdC_6PJZAI7-P3UOF3I,1286
|
277
277
|
vellum/client/types/generate_response.py,sha256=QjbBGFGRE1tHcyDodM6Avzq0YHI4gsprkAWpdqZRrh4,1135
|
@@ -304,7 +304,7 @@ vellum/client/types/initiated_workflow_node_result_event.py,sha256=Nu1J4iQYsW2HH
|
|
304
304
|
vellum/client/types/instructor_vectorizer_config.py,sha256=7udlosXv4CUWTW_Q9m0mz3VRi1FKSbBhDIOhtxRd0-U,731
|
305
305
|
vellum/client/types/instructor_vectorizer_config_request.py,sha256=6LGFFQKntMfX7bdetUqEMVdr3KJHEps0oDp2bNmqWbM,738
|
306
306
|
vellum/client/types/iteration_state_enum.py,sha256=83JSh842OJgQiLtNn1KMimy6RlEYRVH3mDmYWS6Ewzo,180
|
307
|
-
vellum/client/types/jinja_prompt_block.py,sha256
|
307
|
+
vellum/client/types/jinja_prompt_block.py,sha256=-wukO4TaDvGlrbkg9ZtDOPEfoLmaoX7830ktLGHRrvI,939
|
308
308
|
vellum/client/types/json_input.py,sha256=ZUA2O9YueBCx0IMMdB8uYNSWJiSDZxMm5ogwbwCmz_g,761
|
309
309
|
vellum/client/types/json_input_request.py,sha256=x5sA-VXxF4QH-98xRcIKPZhsMVbnJNUQofiUQqyfGk4,768
|
310
310
|
vellum/client/types/json_variable_value.py,sha256=X7eBEWxuozfvIdqD5sIZ5L-L77Ou6IIsZaQVNXh5G2k,634
|
@@ -392,7 +392,7 @@ vellum/client/types/paginated_test_suite_test_case_list.py,sha256=9KrCCQKy0egMmV
|
|
392
392
|
vellum/client/types/paginated_workflow_release_tag_read_list.py,sha256=dH24ESWyAMVtyHsBkxG8kJ9oORY04Wn3IN-7jvV7Lu4,818
|
393
393
|
vellum/client/types/pdf_search_result_meta_source.py,sha256=EMVhqdN1bwE6Ujdx4VhlmKQtJvitN-57kY8oZPxh9dI,1126
|
394
394
|
vellum/client/types/pdf_search_result_meta_source_request.py,sha256=nUhaD2Kw1paGC6O_ICVNu3R0e1SVgTshRTkGNgmcjXo,1133
|
395
|
-
vellum/client/types/plain_text_prompt_block.py,sha256=
|
395
|
+
vellum/client/types/plain_text_prompt_block.py,sha256=cqEN-B4mcvMw_9lBN7FQG8pk9b5LBJ9xpM6PTgkGiqs,930
|
396
396
|
vellum/client/types/price.py,sha256=ewzXDBVLaleuXMVQ-gQ3G1Nl5J2OWOVEMEFfnQIpiTk,610
|
397
397
|
vellum/client/types/processing_failure_reason_enum.py,sha256=R_KIW7TcQejhc-vLhtNf9SdkYADgoZCn4ch4_RRIvsI,195
|
398
398
|
vellum/client/types/prompt_block.py,sha256=MIsxxmAmuT0thkkG12xm3THO5dlRLbFeMZBVTSvb788,493
|
@@ -424,7 +424,7 @@ vellum/client/types/rejected_workflow_node_result_event.py,sha256=o9AUc9hT60F8ck
|
|
424
424
|
vellum/client/types/release_tag_source.py,sha256=YavosOXZ976yfXTNWRTZwh2HhRiYmSDk0bQCkl-jCoQ,158
|
425
425
|
vellum/client/types/replace_test_suite_test_case_request.py,sha256=c1GT1RUCei1yWxyZy4Gv40PkXYisvK5OkzlqQ6WeBYA,1906
|
426
426
|
vellum/client/types/rich_text_child_block.py,sha256=X_ACKFKSUx5SXT1cLp0Y5-7VrNxcGOggPm67Lk2442U,270
|
427
|
-
vellum/client/types/rich_text_prompt_block.py,sha256=
|
427
|
+
vellum/client/types/rich_text_prompt_block.py,sha256=_Y2tRnSDtOUYHcq7zNfYbgqcLZab-Zd-Yh6UKdybVIY,1036
|
428
428
|
vellum/client/types/sandbox_scenario.py,sha256=f4S0tDxmPYHIrD_BMjRL3XZGcGxlWy9apfI64hV7MBE,844
|
429
429
|
vellum/client/types/scenario_input.py,sha256=caC8p5ZnuXhv2ZiHvzTvKL3Ebtr4NXP9Q8UcJEVB8-U,476
|
430
430
|
vellum/client/types/scenario_input_chat_history_variable_value.py,sha256=ihgJktmZpz_12wx2yPtyrSrBjcBcSg9fby7h_eLoXgI,835
|
@@ -566,7 +566,7 @@ vellum/client/types/token_overlapping_window_chunking_request.py,sha256=IjCs9UDr
|
|
566
566
|
vellum/client/types/unit_enum.py,sha256=BKWRVp2WfHtGK4D6TsolhNJHGHfExzrRHkFn8H8QkwQ,113
|
567
567
|
vellum/client/types/upload_document_response.py,sha256=6_5Cm4yBPq5nD-rEql6GsmrAtSVVtNRczOL5YwsBVMI,649
|
568
568
|
vellum/client/types/upsert_test_suite_test_case_request.py,sha256=iB38vx4mo4yNLV5XTeXMGR-PJLOQPloWQOAAi7PDpM0,2079
|
569
|
-
vellum/client/types/variable_prompt_block.py,sha256=
|
569
|
+
vellum/client/types/variable_prompt_block.py,sha256=PZxTq_zu7wa5r2eTBbwCRL7hwDKaLaBT3URB1RR2WKw,946
|
570
570
|
vellum/client/types/vellum_audio.py,sha256=oPm1bcxk7fTfWfHWOPSLvrZrRBjCyPDVDRMACPoWmMI,721
|
571
571
|
vellum/client/types/vellum_audio_request.py,sha256=y9CZgQ1TteW0AHNk8GuAZLNVFa981rh7P9vyV8bfgys,728
|
572
572
|
vellum/client/types/vellum_error.py,sha256=jCKfuCkDTiyFb1-QyP2cg0wReja6wMuooKPAjNhBA0M,643
|
@@ -1297,7 +1297,7 @@ vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org
|
|
1297
1297
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=EvylK1rGKpd4iiooEW9O5A9Q8DMTtBwETe_GtQT8M-E,2139
|
1298
1298
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
|
1299
1299
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=fnjiRWLoRlC4Puo5oQcpZD5Hd-EesxsAo9l5tGAkpZQ,270
|
1300
|
-
vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=
|
1300
|
+
vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=fypgmZHgaDtGqSBC8rjYiyryJ0H58LPt_CafLfAprO0,6341
|
1301
1301
|
vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=zdpNJoawB5PedsCCfgOGDDoWuif0jNtlV-K9sFL6cNQ,4968
|
1302
1302
|
vellum/workflows/nodes/displayable/bases/search_node.py,sha256=pqiui8G6l_9FLE1HH4rCdFC73Bl7_AIBAmQQMjqe190,3570
|
1303
1303
|
vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
|
@@ -1316,7 +1316,7 @@ vellum/workflows/nodes/displayable/guardrail_node/node.py,sha256=7Ep7Ff7FtFry3Jw
|
|
1316
1316
|
vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py,sha256=gSUOoEZLlrx35-tQhSAd3An8WDwBqyiQh-sIebLU9wU,74
|
1317
1317
|
vellum/workflows/nodes/displayable/inline_prompt_node/node.py,sha256=dTnP1yH1P0NqMw3noxt9XwaDCpX8ZOhuvVYNAn_DdCQ,2119
|
1318
1318
|
vellum/workflows/nodes/displayable/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1319
|
-
vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=
|
1319
|
+
vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=P1DUL0wIG-cyA5dqGv7242cFWJXysmombdujKrJtl7k,4669
|
1320
1320
|
vellum/workflows/nodes/displayable/merge_node/__init__.py,sha256=J8IC08dSH7P76wKlNuxe1sn7toNGtSQdFirUbtPDEs0,60
|
1321
1321
|
vellum/workflows/nodes/displayable/merge_node/node.py,sha256=ZyPvcTgfPOneOm5Dc2kUOoPkwNJqwRPZSj232akXynA,324
|
1322
1322
|
vellum/workflows/nodes/displayable/note_node/__init__.py,sha256=KWA3P4fyYJ-fOTky8qNGlcOotQ-HeHJ9AjZt6mRQmCE,58
|
@@ -1328,7 +1328,7 @@ vellum/workflows/nodes/displayable/search_node/node.py,sha256=yhFWulbNmSQoDAwtTS
|
|
1328
1328
|
vellum/workflows/nodes/displayable/subworkflow_deployment_node/__init__.py,sha256=9yYM6001YZeqI1VOk1QuEM_yrffk_EdsO7qaPzINKds,92
|
1329
1329
|
vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py,sha256=pnbRCgdzWXrXhm5jDkDDASl5xu5w3DxskC34yJVmWUs,7147
|
1330
1330
|
vellum/workflows/nodes/displayable/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1331
|
-
vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py,sha256=
|
1331
|
+
vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py,sha256=UI_RMmXn9qwB-StnFPvkDd9FctBQAg43wrfouqvPepk,4701
|
1332
1332
|
vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=4CMwDtXwTaEvFfDpA6j2iLqc7S6IICSkvVZOobEpeps,6954
|
1333
1333
|
vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=KqKJtJ0vuNoPuUPMdILmBTt4a2fBBxxun-nmOI7T8jo,2585
|
1334
1334
|
vellum/workflows/nodes/utils.py,sha256=EZt7CzJmgQBR_GWFpZr8d-oaoti3tolTd2Cv9wm7dKo,1087
|
@@ -1353,6 +1353,7 @@ vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPyc
|
|
1353
1353
|
vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
|
1354
1354
|
vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
|
1355
1355
|
vellum/workflows/runner/runner.py,sha256=RXnLEmSJFbp0u4vKF7rvD2fscuYfcRYkspIJINnvFAI,27607
|
1356
|
+
vellum/workflows/sandbox.py,sha256=wNyOfd3gb6-O85EQcBIHNCnSYPH7Oufh2z4hQnR2HFU,2059
|
1356
1357
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
1357
1358
|
vellum/workflows/state/base.py,sha256=jpSzF1OQd3-fqi6dMGlNsQl-7JnJxCdzWIigmX8Wz-I,14425
|
1358
1359
|
vellum/workflows/state/context.py,sha256=oXiEdNsWJi1coRB85IreTgUeR6_CrWWBXndtLff9S7M,1272
|
@@ -1360,6 +1361,8 @@ vellum/workflows/state/encoder.py,sha256=kRrqwD0vFCiSRZR3rg8Sjkh8sDEerQQhlvmdSYQ
|
|
1360
1361
|
vellum/workflows/state/store.py,sha256=VYGBQgN1bpd1as5eGiouV_7scg8QsRs4_1aqZAGIsRQ,793
|
1361
1362
|
vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1362
1363
|
vellum/workflows/state/tests/test_state.py,sha256=BQjcdREIK1MPuGhivRUgpynVJLftjEpH9RG3cRKxQEY,3310
|
1364
|
+
vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1365
|
+
vellum/workflows/tests/test_sandbox.py,sha256=OMN15LuFUV58-XnAhtBiLcEjNt-Pj6bZgetDr8miCiw,1808
|
1363
1366
|
vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
|
1364
1367
|
vellum/workflows/types/core.py,sha256=D2NcSBwGgWj_mtXZqe3KnEQcb5qd5HzqAwnxwmlCfCw,899
|
1365
1368
|
vellum/workflows/types/generics.py,sha256=ZkfoRhWs042i5IjA99v2wIhmh1u-Wieo3LzosgGWJVk,600
|
@@ -1368,10 +1371,10 @@ vellum/workflows/types/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
1368
1371
|
vellum/workflows/types/tests/test_utils.py,sha256=t6eqTSZqw2oVkIw6lP393qVmuyzjZZ62Ls1iELwZg_o,2434
|
1369
1372
|
vellum/workflows/types/utils.py,sha256=Vk_itjV8YrfoT_Pm_x7QMvBdpCTr_XBTlszqZkQQJLw,5587
|
1370
1373
|
vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1371
|
-
vellum/workflows/utils/functions.py,sha256=
|
1374
|
+
vellum/workflows/utils/functions.py,sha256=7A4BImhtY__qQpNrF5uPiwLfkj6PSUxYvF7ITigIkxY,4051
|
1372
1375
|
vellum/workflows/utils/names.py,sha256=QLUqfJ1tmSEeUwBKTTiv_Qk3QGbInC2RSmlXfGXc8Wo,380
|
1373
1376
|
vellum/workflows/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1374
|
-
vellum/workflows/utils/tests/test_functions.py,sha256=
|
1377
|
+
vellum/workflows/utils/tests/test_functions.py,sha256=ytdruS55aO2egsb5sAv8_9jf68L1cJoZu2uKV2iamrg,8083
|
1375
1378
|
vellum/workflows/utils/tests/test_names.py,sha256=aOqpyvMsOEK_9mg_-yaNxQDW7QQfwqsYs37PseyLhxw,402
|
1376
1379
|
vellum/workflows/utils/tests/test_uuids.py,sha256=i77ABQ0M3S-aFLzDXHJq_yr5FPkJEWCMBn1HJ3DObrE,437
|
1377
1380
|
vellum/workflows/utils/tests/test_vellum_variables.py,sha256=0ISy1xjY7_rci0Mt_naK0xrmurE1REZLMCgPOCLFKRM,776
|
@@ -1381,8 +1384,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
|
|
1381
1384
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1382
1385
|
vellum/workflows/workflows/base.py,sha256=zpspOEdO5Ye_0ZvN-Wkzv9iQSiF1sD201ba8lhbnPbs,17086
|
1383
1386
|
vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
|
1384
|
-
vellum_ai-0.12.
|
1385
|
-
vellum_ai-0.12.
|
1386
|
-
vellum_ai-0.12.
|
1387
|
-
vellum_ai-0.12.
|
1388
|
-
vellum_ai-0.12.
|
1387
|
+
vellum_ai-0.12.6.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1388
|
+
vellum_ai-0.12.6.dist-info/METADATA,sha256=C1VU1fn4OEzUUW4pv7NTqN6hMP4XCwxUZldj2hAtths,5128
|
1389
|
+
vellum_ai-0.12.6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1390
|
+
vellum_ai-0.12.6.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1391
|
+
vellum_ai-0.12.6.dist-info/RECORD,,
|
vellum_cli/__init__.py
CHANGED
@@ -125,10 +125,16 @@ Should only be used for debugging purposes.""",
|
|
125
125
|
help="""Exclude the code definition of each Resource from the pull response. \
|
126
126
|
Should only be used for debugging purposes.""",
|
127
127
|
)
|
128
|
+
@click.option(
|
129
|
+
"--strict",
|
130
|
+
is_flag=True,
|
131
|
+
help="""Raises an error immediately if there are any issues with the pulling of the Resource.""",
|
132
|
+
)
|
128
133
|
def pull(
|
129
134
|
ctx: click.Context,
|
130
135
|
include_json: Optional[bool],
|
131
136
|
exclude_code: Optional[bool],
|
137
|
+
strict: Optional[bool],
|
132
138
|
) -> None:
|
133
139
|
"""Pull Resources from Vellum"""
|
134
140
|
|
@@ -136,6 +142,7 @@ def pull(
|
|
136
142
|
pull_command(
|
137
143
|
include_json=include_json,
|
138
144
|
exclude_code=exclude_code,
|
145
|
+
strict=strict,
|
139
146
|
)
|
140
147
|
|
141
148
|
|
@@ -159,12 +166,18 @@ Should only be used for debugging purposes.""",
|
|
159
166
|
help="""Exclude the code definition of the Workflow from the pull response. \
|
160
167
|
Should only be used for debugging purposes.""",
|
161
168
|
)
|
169
|
+
@click.option(
|
170
|
+
"--strict",
|
171
|
+
is_flag=True,
|
172
|
+
help="""Raises an error immediately if there are any issues with the pulling of the Workflow.""",
|
173
|
+
)
|
162
174
|
def workflows_pull(
|
163
175
|
module: Optional[str],
|
164
176
|
include_json: Optional[bool],
|
165
177
|
workflow_sandbox_id: Optional[str],
|
166
178
|
workflow_deployment: Optional[str],
|
167
179
|
exclude_code: Optional[bool],
|
180
|
+
strict: Optional[bool],
|
168
181
|
) -> None:
|
169
182
|
"""
|
170
183
|
Pull Workflows from Vellum. If a module is provided, only the Workflow for that module will be pulled.
|
@@ -177,6 +190,7 @@ def workflows_pull(
|
|
177
190
|
workflow_sandbox_id=workflow_sandbox_id,
|
178
191
|
workflow_deployment=workflow_deployment,
|
179
192
|
exclude_code=exclude_code,
|
193
|
+
strict=strict,
|
180
194
|
)
|
181
195
|
|
182
196
|
|
vellum_cli/pull.py
CHANGED
@@ -13,6 +13,8 @@ from vellum.workflows.vellum_client import create_vellum_client
|
|
13
13
|
from vellum_cli.config import VellumCliConfig, WorkflowConfig, load_vellum_cli_config
|
14
14
|
from vellum_cli.logger import load_cli_logger
|
15
15
|
|
16
|
+
ERROR_LOG_FILE_NAME = "error.log"
|
17
|
+
|
16
18
|
|
17
19
|
def _is_valid_uuid(val: Union[str, UUID, None]) -> bool:
|
18
20
|
try:
|
@@ -81,6 +83,7 @@ def pull_command(
|
|
81
83
|
workflow_deployment: Optional[str] = None,
|
82
84
|
include_json: Optional[bool] = None,
|
83
85
|
exclude_code: Optional[bool] = None,
|
86
|
+
strict: Optional[bool] = None,
|
84
87
|
) -> None:
|
85
88
|
load_dotenv()
|
86
89
|
logger = load_cli_logger()
|
@@ -109,6 +112,8 @@ def pull_command(
|
|
109
112
|
query_parameters["include_json"] = include_json
|
110
113
|
if exclude_code:
|
111
114
|
query_parameters["exclude_code"] = exclude_code
|
115
|
+
if strict:
|
116
|
+
query_parameters["strict"] = strict
|
112
117
|
|
113
118
|
response = client.workflows.pull(
|
114
119
|
pk,
|
@@ -119,6 +124,7 @@ def pull_command(
|
|
119
124
|
zip_buffer = io.BytesIO(zip_bytes)
|
120
125
|
|
121
126
|
target_dir = os.path.join(os.getcwd(), *workflow_config.module.split("."))
|
127
|
+
error_content = ""
|
122
128
|
with zipfile.ZipFile(zip_buffer) as zip_file:
|
123
129
|
# Delete files in target_dir that aren't in the zip file
|
124
130
|
if os.path.exists(target_dir):
|
@@ -146,8 +152,13 @@ def pull_command(
|
|
146
152
|
target_file = os.path.join(target_dir, file_name)
|
147
153
|
os.makedirs(os.path.dirname(target_file), exist_ok=True)
|
148
154
|
with zip_file.open(file_name) as source, open(target_file, "w") as target:
|
155
|
+
content = source.read().decode("utf-8")
|
156
|
+
if file_name == ERROR_LOG_FILE_NAME:
|
157
|
+
error_content = content
|
158
|
+
continue
|
159
|
+
|
149
160
|
logger.info(f"Writing to {target_file}...")
|
150
|
-
target.write(
|
161
|
+
target.write(content)
|
151
162
|
|
152
163
|
if include_json:
|
153
164
|
logger.warning(
|
@@ -158,4 +169,7 @@ Its schema should be considered unstable and subject to change at any time."""
|
|
158
169
|
if save_lock_file:
|
159
170
|
config.save()
|
160
171
|
|
161
|
-
|
172
|
+
if error_content:
|
173
|
+
logger.error(error_content)
|
174
|
+
else:
|
175
|
+
logger.info(f"Successfully pulled Workflow into {workflow_config.module}")
|
vellum_cli/tests/test_pull.py
CHANGED
@@ -441,3 +441,48 @@ def test_pull__sandbox_id_with_other_workflow_deployment_in_lock(vellum_client,
|
|
441
441
|
"deployments": [],
|
442
442
|
},
|
443
443
|
]
|
444
|
+
|
445
|
+
|
446
|
+
def test_pull__handle_error_log(vellum_client, mock_module):
|
447
|
+
# GIVEN a pyproject.toml with a workflow configured
|
448
|
+
temp_dir = mock_module.temp_dir
|
449
|
+
module = mock_module.module
|
450
|
+
workflow_sandbox_id = mock_module.workflow_sandbox_id
|
451
|
+
|
452
|
+
# AND the workflow pull API call returns a zip file with an error log
|
453
|
+
vellum_client.workflows.pull.return_value = iter(
|
454
|
+
[_zip_file_map({"workflow.py": "print('hello')", "error.log": "test error"})]
|
455
|
+
)
|
456
|
+
|
457
|
+
# WHEN the user runs the pull command with the new workflow sandbox id
|
458
|
+
runner = CliRunner()
|
459
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-sandbox-id", workflow_sandbox_id])
|
460
|
+
|
461
|
+
# THEN the command returns successfully
|
462
|
+
assert result.exit_code == 0
|
463
|
+
|
464
|
+
# AND the error log file is not written to the module directory
|
465
|
+
assert not os.path.exists(os.path.join(temp_dir, *module.split("."), "error.log"))
|
466
|
+
|
467
|
+
# AND the error log is printed to the console
|
468
|
+
assert result.output.endswith("\x1b[31;20mtest error\x1b[0m\n")
|
469
|
+
|
470
|
+
|
471
|
+
def test_pull__strict__with_error(vellum_client, mock_module):
|
472
|
+
# GIVEN a pyproject.toml with a workflow configured
|
473
|
+
workflow_sandbox_id = mock_module.workflow_sandbox_id
|
474
|
+
|
475
|
+
# AND the workflow pull API call returns a zip file
|
476
|
+
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
477
|
+
|
478
|
+
# WHEN the user runs the pull command with the new workflow sandbox id
|
479
|
+
runner = CliRunner()
|
480
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--strict", "--workflow-sandbox-id", workflow_sandbox_id])
|
481
|
+
|
482
|
+
# THEN the command returns successfully
|
483
|
+
assert result.exit_code == 0
|
484
|
+
|
485
|
+
# AND the pull api is called with strict=True
|
486
|
+
vellum_client.workflows.pull.assert_called_once()
|
487
|
+
call_args = vellum_client.workflows.pull.call_args.kwargs
|
488
|
+
assert call_args["request_options"]["additional_query_parameters"] == {"strict": True}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import ClassVar, Dict, Optional
|
2
|
+
from typing import ClassVar, Dict, Optional, Union
|
3
3
|
|
4
4
|
from vellum.workflows.nodes.utils import get_unadorned_node
|
5
5
|
from vellum.workflows.ports import Port
|
@@ -18,7 +18,7 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
|
18
18
|
target_handle_id: ClassVar[Optional[UUID]] = None
|
19
19
|
|
20
20
|
# Used to explicitly set the node input ids by name for a node
|
21
|
-
node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
21
|
+
node_input_ids_by_name: ClassVar[Dict[str, Union[UUID, str]]] = {}
|
22
22
|
|
23
23
|
def _get_node_display_uuid(self, attribute: str) -> UUID:
|
24
24
|
explicit_value = self._get_explicit_node_display_attr(attribute, UUID)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import inspect
|
2
2
|
from uuid import UUID
|
3
|
-
from typing import ClassVar,
|
3
|
+
from typing import ClassVar, Generic, Optional, TypeVar
|
4
4
|
|
5
5
|
from vellum.workflows.nodes.displayable.code_execution_node import CodeExecutionNode
|
6
6
|
from vellum.workflows.nodes.displayable.code_execution_node.utils import read_file_from_path
|
@@ -21,8 +21,6 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
|
|
21
21
|
output_id: ClassVar[Optional[UUID]] = None
|
22
22
|
log_output_id: ClassVar[Optional[UUID]] = None
|
23
23
|
|
24
|
-
node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
25
|
-
|
26
24
|
def serialize(
|
27
25
|
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
|
28
26
|
) -> JsonObject:
|
@@ -40,17 +40,17 @@ _ConditionalNodeType = TypeVar("_ConditionalNodeType", bound=ConditionalNode)
|
|
40
40
|
|
41
41
|
@dataclass
|
42
42
|
class RuleIdMap:
|
43
|
-
id:
|
43
|
+
id: str
|
44
44
|
lhs: Optional["RuleIdMap"]
|
45
45
|
rhs: Optional["RuleIdMap"]
|
46
|
-
field_node_input_id: Optional[
|
47
|
-
value_node_input_id: Optional[
|
46
|
+
field_node_input_id: Optional[str]
|
47
|
+
value_node_input_id: Optional[str]
|
48
48
|
|
49
49
|
|
50
50
|
@dataclass
|
51
51
|
class ConditionId:
|
52
|
-
id:
|
53
|
-
rule_group_id: Optional[
|
52
|
+
id: str
|
53
|
+
rule_group_id: Optional[str]
|
54
54
|
|
55
55
|
|
56
56
|
class BaseConditionalNodeDisplay(BaseNodeVellumDisplay[_ConditionalNodeType], Generic[_ConditionalNodeType]):
|
@@ -112,7 +112,7 @@ but the defined conditions have length {len(condition_ids)}"""
|
|
112
112
|
node_id, f"{current_id}.field", descriptor._expression, display_context, field_node_input_id
|
113
113
|
)
|
114
114
|
node_inputs.append(expression_node_input)
|
115
|
-
field_node_input_id =
|
115
|
+
field_node_input_id = expression_node_input.id
|
116
116
|
operator = self._convert_descriptor_to_operator(descriptor)
|
117
117
|
|
118
118
|
elif isinstance(descriptor, (BetweenExpression, NotBetweenExpression)):
|
@@ -128,8 +128,8 @@ but the defined conditions have length {len(condition_ids)}"""
|
|
128
128
|
)
|
129
129
|
node_inputs.extend([field_node_input, value_node_input])
|
130
130
|
operator = self._convert_descriptor_to_operator(descriptor)
|
131
|
-
field_node_input_id =
|
132
|
-
value_node_input_id =
|
131
|
+
field_node_input_id = field_node_input.id
|
132
|
+
value_node_input_id = value_node_input.id
|
133
133
|
|
134
134
|
else:
|
135
135
|
lhs = descriptor._lhs # type: ignore[attr-defined]
|
@@ -145,19 +145,19 @@ but the defined conditions have length {len(condition_ids)}"""
|
|
145
145
|
node_id, f"{current_id}.value", rhs, display_context, value_node_input_id
|
146
146
|
)
|
147
147
|
node_inputs.append(rhs_node_input)
|
148
|
-
value_node_input_id =
|
148
|
+
value_node_input_id = rhs_node_input.id
|
149
149
|
|
150
150
|
operator = self._convert_descriptor_to_operator(descriptor)
|
151
|
-
field_node_input_id =
|
151
|
+
field_node_input_id = lhs_node_input.id
|
152
152
|
|
153
153
|
return {
|
154
|
-
"id":
|
154
|
+
"id": current_id,
|
155
155
|
"rules": None,
|
156
156
|
"combinator": None,
|
157
157
|
"negated": False,
|
158
|
-
"field_node_input_id":
|
158
|
+
"field_node_input_id": field_node_input_id,
|
159
159
|
"operator": operator,
|
160
|
-
"value_node_input_id":
|
160
|
+
"value_node_input_id": value_node_input_id,
|
161
161
|
}
|
162
162
|
|
163
163
|
conditions = []
|
@@ -263,7 +263,7 @@ but the defined conditions have length {len(condition_ids)}"""
|
|
263
263
|
|
264
264
|
def get_nested_rule_details_by_path(
|
265
265
|
self, rule_ids: List[RuleIdMap], path: List[int]
|
266
|
-
) -> Union[Tuple[
|
266
|
+
) -> Union[Tuple[str, Optional[str], Optional[str]], None]:
|
267
267
|
current_rule = rule_ids[path[0]]
|
268
268
|
|
269
269
|
for step in path[1:]:
|
@@ -284,11 +284,11 @@ but the defined conditions have length {len(condition_ids)}"""
|
|
284
284
|
|
285
285
|
return None
|
286
286
|
|
287
|
-
def _generate_hash_for_rule_ids(self, node_id, rule_id) -> Tuple[
|
287
|
+
def _generate_hash_for_rule_ids(self, node_id, rule_id) -> Tuple[str, str, str]:
|
288
288
|
return (
|
289
|
-
uuid4_from_hash(f"{node_id}|{rule_id}|current"),
|
290
|
-
uuid4_from_hash(f"{node_id}|{rule_id}||field"),
|
291
|
-
uuid4_from_hash(f"{node_id}|{rule_id}||value"),
|
289
|
+
str(uuid4_from_hash(f"{node_id}|{rule_id}|current")),
|
290
|
+
str(uuid4_from_hash(f"{node_id}|{rule_id}||field")),
|
291
|
+
str(uuid4_from_hash(f"{node_id}|{rule_id}||value")),
|
292
292
|
)
|
293
293
|
|
294
294
|
def _get_source_handle_ids(self) -> Dict[int, UUID]:
|
@@ -28,7 +28,9 @@ class VariableIdMap:
|
|
28
28
|
|
29
29
|
|
30
30
|
class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_SearchNodeType]):
|
31
|
-
|
31
|
+
# A mapping between the id of the operand (e.g. "lhs_variable_id" or "rhs_variable_id") and the id of the node input
|
32
|
+
# that the operand is pointing to.
|
33
|
+
metadata_filter_input_id_by_operand_id: Dict[UUID, UUID] = {}
|
32
34
|
|
33
35
|
def serialize(
|
34
36
|
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
|
@@ -149,18 +151,20 @@ class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_Sea
|
|
149
151
|
variables,
|
150
152
|
)
|
151
153
|
elif isinstance(logical_expression, VellumValueLogicalConditionRequest):
|
152
|
-
lhs_variable_id =
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
154
|
+
lhs_variable_id = logical_expression.lhs_variable.value
|
155
|
+
if not isinstance(lhs_variable_id, str):
|
156
|
+
raise TypeError(f"Expected lhs_variable_id to be a string, got {type(lhs_variable_id)}")
|
157
|
+
|
158
|
+
rhs_variable_id = logical_expression.rhs_variable.value
|
159
|
+
if not isinstance(rhs_variable_id, str):
|
160
|
+
raise TypeError(f"Expected rhs_variable_id to be a string, got {type(rhs_variable_id)}")
|
161
|
+
|
162
|
+
lhs_query_input_id: UUID = self.metadata_filter_input_id_by_operand_id.get(
|
163
|
+
UUID(lhs_variable_id)
|
164
|
+
) or uuid4_from_hash(f"{self.node_id}|{hash(tuple(path))}")
|
165
|
+
rhs_query_input_id: UUID = self.metadata_filter_input_id_by_operand_id.get(
|
166
|
+
UUID(rhs_variable_id)
|
167
|
+
) or uuid4_from_hash(f"{self.node_id}|{hash(tuple(path))}")
|
164
168
|
|
165
169
|
return (
|
166
170
|
{
|
@@ -173,7 +177,7 @@ class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_Sea
|
|
173
177
|
create_node_input(
|
174
178
|
self.node_id,
|
175
179
|
f"vellum-query-builder-variable-{lhs_variable_id}",
|
176
|
-
lhs_query_input_id,
|
180
|
+
str(lhs_query_input_id),
|
177
181
|
display_context,
|
178
182
|
input_id=UUID(lhs_variable_id),
|
179
183
|
pointer_type=InputVariablePointer,
|
@@ -181,7 +185,7 @@ class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_Sea
|
|
181
185
|
create_node_input(
|
182
186
|
self.node_id,
|
183
187
|
f"vellum-query-builder-variable-{rhs_variable_id}",
|
184
|
-
rhs_query_input_id,
|
188
|
+
str(rhs_query_input_id),
|
185
189
|
display_context,
|
186
190
|
input_id=UUID(rhs_variable_id),
|
187
191
|
pointer_type=InputVariablePointer,
|
@@ -25,7 +25,7 @@ class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Gene
|
|
25
25
|
|
26
26
|
template_input_id = self.template_input_id or next(
|
27
27
|
(
|
28
|
-
input_id
|
28
|
+
UUID(input_id) if isinstance(input_id, str) else input_id
|
29
29
|
for input_name, input_id in self.node_input_ids_by_name.items()
|
30
30
|
if input_name == TEMPLATE_INPUT_NAME
|
31
31
|
),
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import Any, List, Optional, Type, cast
|
2
|
+
from typing import Any, List, Optional, Type, Union, cast
|
3
3
|
|
4
4
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
5
5
|
from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
|
@@ -27,10 +27,10 @@ def create_node_input(
|
|
27
27
|
input_name: str,
|
28
28
|
value: Any,
|
29
29
|
display_context: WorkflowDisplayContext,
|
30
|
-
input_id: Optional[UUID],
|
30
|
+
input_id: Union[Optional[UUID], Optional[str]],
|
31
31
|
pointer_type: Optional[Type[NodeInputValuePointerRule]] = ConstantValuePointer,
|
32
32
|
) -> NodeInput:
|
33
|
-
input_id = input_id
|
33
|
+
input_id = str(input_id) if input_id else str(uuid4_from_hash(f"{node_id}|{input_name}"))
|
34
34
|
if (
|
35
35
|
isinstance(value, OutputReference)
|
36
36
|
and value.outputs_class._node_class
|
@@ -42,7 +42,7 @@ def create_node_input(
|
|
42
42
|
|
43
43
|
rules = create_node_input_value_pointer_rules(value, display_context, pointer_type=pointer_type)
|
44
44
|
return NodeInput(
|
45
|
-
id=
|
45
|
+
id=input_id,
|
46
46
|
key=input_name,
|
47
47
|
value=NodeInputValuePointer(
|
48
48
|
rules=rules,
|
File without changes
|
File without changes
|
File without changes
|