vellum-ai 0.12.4__py3-none-any.whl → 0.12.6__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/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
|