vellum-ai 0.11.9__py3-none-any.whl → 0.11.10__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,7 @@ class BaseClientWrapper:
17
17
  headers: typing.Dict[str, str] = {
18
18
  "X-Fern-Language": "Python",
19
19
  "X-Fern-SDK-Name": "vellum-ai",
20
- "X-Fern-SDK-Version": "0.11.9",
20
+ "X-Fern-SDK-Version": "0.11.10",
21
21
  }
22
22
  headers["X_API_KEY"] = self.api_key
23
23
  return headers
@@ -73,7 +73,8 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
73
73
  request_options: Optional[RequestOptions] = None - The request options to use for the custom script.
74
74
  """
75
75
 
76
- filepath: ClassVar[str]
76
+ filepath: ClassVar[Optional[str]] = None
77
+ code: ClassVar[Optional[str]] = None
77
78
 
78
79
  code_inputs: ClassVar[EntityInputsInterface]
79
80
  runtime: CodeExecutionRuntime = "PYTHON_3_11_6"
@@ -190,6 +191,21 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
190
191
  return compiled_inputs
191
192
 
192
193
  def _resolve_code(self) -> str:
194
+ if self.code and self.filepath:
195
+ raise NodeException(
196
+ message="Cannot specify both `code` and `filepath` for a CodeExecutionNode",
197
+ code=VellumErrorCode.INVALID_INPUTS,
198
+ )
199
+
200
+ if self.code:
201
+ return self.code
202
+
203
+ if not self.filepath:
204
+ raise NodeException(
205
+ message="Must specify either `code` or `filepath` for a CodeExecutionNode",
206
+ code=VellumErrorCode.INVALID_INPUTS,
207
+ )
208
+
193
209
  root = inspect.getfile(self.__class__)
194
210
  code = read_file_from_path(node_filepath=root, script_filepath=self.filepath)
195
211
  if not code:
@@ -1,6 +1,8 @@
1
+ import pytest
1
2
  import os
2
3
 
3
4
  from vellum import CodeExecutorResponse, NumberVellumValue, StringInput
5
+ from vellum.workflows.exceptions import NodeException
4
6
  from vellum.workflows.inputs.base import BaseInputs
5
7
  from vellum.workflows.nodes.displayable.code_execution_node import CodeExecutionNode
6
8
  from vellum.workflows.references.vellum_secret import VellumSecretReference
@@ -62,6 +64,145 @@ def main(word: str) -> int:
62
64
  )
63
65
 
64
66
 
67
+ def test_run_workflow__code_attribute(vellum_client):
68
+ """Confirm that CodeExecutionNodes can use the `code` attribute to specify the code to execute."""
69
+
70
+ # GIVEN a node that subclasses CodeExecutionNode
71
+ class Inputs(BaseInputs):
72
+ word: str
73
+
74
+ class State(BaseState):
75
+ pass
76
+
77
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, int]):
78
+ code = """\
79
+ def main(word: str) -> int:
80
+ print(word) # noqa: T201
81
+ return len(word)
82
+ """
83
+ runtime = "PYTHON_3_11_6"
84
+
85
+ code_inputs = {
86
+ "word": Inputs.word,
87
+ }
88
+
89
+ # AND we know what the Code Execution Node will respond with
90
+ mock_code_execution = CodeExecutorResponse(
91
+ log="hello",
92
+ output=NumberVellumValue(value=5),
93
+ )
94
+ vellum_client.execute_code.return_value = mock_code_execution
95
+
96
+ # WHEN we run the node
97
+ node = ExampleCodeExecutionNode(
98
+ state=State(
99
+ meta=StateMeta(workflow_inputs=Inputs(word="hello")),
100
+ )
101
+ )
102
+ outputs = node.run()
103
+
104
+ # THEN the node should have produced the outputs we expect
105
+ assert outputs == {"result": 5, "log": "hello"}
106
+
107
+ # AND we should have invoked the Code with the expected inputs
108
+ vellum_client.execute_code.assert_called_once_with(
109
+ input_values=[
110
+ StringInput(name="word", value="hello"),
111
+ ],
112
+ code="""\
113
+ def main(word: str) -> int:
114
+ print(word) # noqa: T201
115
+ return len(word)
116
+ """,
117
+ runtime="PYTHON_3_11_6",
118
+ output_type="NUMBER",
119
+ packages=[],
120
+ request_options=None,
121
+ )
122
+
123
+
124
+ def test_run_workflow__code_and_filepath_defined(vellum_client):
125
+ """Confirm that CodeExecutionNodes raise an error if both `code` and `filepath` are defined."""
126
+
127
+ # GIVEN a node that subclasses CodeExecutionNode
128
+ class Inputs(BaseInputs):
129
+ word: str
130
+
131
+ class State(BaseState):
132
+ pass
133
+
134
+ fixture = os.path.abspath(os.path.join(__file__, "../fixtures/main.py"))
135
+
136
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, int]):
137
+ filepath = fixture
138
+ code = """\
139
+ def main(word: str) -> int:
140
+ print(word) # noqa: T201
141
+ return len(word)
142
+ """
143
+ runtime = "PYTHON_3_11_6"
144
+
145
+ code_inputs = {
146
+ "word": Inputs.word,
147
+ }
148
+
149
+ # AND we know what the Code Execution Node will respond with
150
+ mock_code_execution = CodeExecutorResponse(
151
+ log="hello",
152
+ output=NumberVellumValue(value=5),
153
+ )
154
+ vellum_client.execute_code.return_value = mock_code_execution
155
+
156
+ # WHEN we run the node
157
+ node = ExampleCodeExecutionNode(
158
+ state=State(
159
+ meta=StateMeta(workflow_inputs=Inputs(word="hello")),
160
+ )
161
+ )
162
+ with pytest.raises(NodeException) as exc_info:
163
+ node.run()
164
+
165
+ # THEN the node should have produced the exception we expected
166
+ assert exc_info.value.message == "Cannot specify both `code` and `filepath` for a CodeExecutionNode"
167
+
168
+
169
+ def test_run_workflow__code_and_filepath_not_defined(vellum_client):
170
+ """Confirm that CodeExecutionNodes raise an error if neither `code` nor `filepath` are defined."""
171
+
172
+ # GIVEN a node that subclasses CodeExecutionNode
173
+ class Inputs(BaseInputs):
174
+ word: str
175
+
176
+ class State(BaseState):
177
+ pass
178
+
179
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, int]):
180
+ runtime = "PYTHON_3_11_6"
181
+
182
+ code_inputs = {
183
+ "word": Inputs.word,
184
+ }
185
+
186
+ # AND we know what the Code Execution Node will respond with
187
+ mock_code_execution = CodeExecutorResponse(
188
+ log="hello",
189
+ output=NumberVellumValue(value=5),
190
+ )
191
+ vellum_client.execute_code.return_value = mock_code_execution
192
+
193
+ # WHEN we run the node
194
+ node = ExampleCodeExecutionNode(
195
+ state=State(
196
+ meta=StateMeta(workflow_inputs=Inputs(word="hello")),
197
+ )
198
+ )
199
+ with pytest.raises(NodeException) as exc_info:
200
+ node.run()
201
+
202
+ # THEN the node should have produced the exception we expected
203
+ assert exc_info.value.message == "Must specify either `code` or `filepath` for a CodeExecutionNode"
204
+
205
+
65
206
  def test_run_workflow__vellum_secret(vellum_client):
66
207
  """Confirm that CodeExecutionNodes can use Vellum Secrets"""
67
208
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.11.9
3
+ Version: 0.11.10
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.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=3W7X1V2Lv0k6djYp60LDu-0lYVMNsEjPXmNmIQ4UW6s,5961
23
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=HoD3AGCMXKoHyyRJteUYlQ7DR26Srjhlrv4fZlLCyKc,1649
23
+ vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=F30B7L3HJLdeYzQmn17KVduWWXPb3KFvFFEVltYWxvY,2124
24
24
  vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=xg6DWEm8CDiOn_fX74fT2XtSEDCnq06dHKj7HX9JnJw,907
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=0y20AAqDivg58hQjHbiCPjEdEghfWmWr-NdYw3u-AwM,1054
@@ -28,7 +28,7 @@ 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=HzB4zQ6MYlRII9GsZcBPzOswFUuwBjn-b3FuDLNyujg,4025
31
+ vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=qJXUTVGfL88Katxu6HQb4yCPUzBFTBFDXcf8pdId3U8,4076
32
32
  vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=gUbSP8_oSAMNIb0CGiefd2FMYgoO6wMoG6iA1FakMjk,13293
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=UezalObmZ3mcg7Nou2RgiI_0cmc7_tSdZLNB591iCcI,2772
@@ -73,13 +73,13 @@ vellum_ee/workflows/display/vellum.py,sha256=OSv0ZS50h1zJbunJ9TH7VEWFw-exXdK_Zsd
73
73
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
74
74
  vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=HkakkrNgVFoHlUP7yHlQjHOvii3CZ90iyU1062PfoW4,12819
75
75
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=AMxNnTm2z3LIR5rqxoCAfuy37F2FTuSRDVtKUoezO8M,1184
76
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=UiE1vJ4nzt1tHAxvXSt8qnV101I__gd5YdiPuKA4TWk,17370
76
+ vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=DWaiYdP138kN3fbQvloh6VEBPlxzd6zSLydc6NpYu3s,17145
77
77
  vellum/__init__.py,sha256=QmGeEPXeFxgkZa849KKK3wH3Y641wyt00Rytfay6KiM,35520
78
78
  vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
79
79
  vellum/client/__init__.py,sha256=o4m7iRZWEV8rP3GkdaztHAjNmjxjWERlarviFoHzuKI,110927
80
80
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
81
81
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
82
- vellum/client/core/client_wrapper.py,sha256=7ETrjiHmBNoSjt9dOpoOQoV1KfdagFOllIIAwyOIi8U,1890
82
+ vellum/client/core/client_wrapper.py,sha256=6zb4goHdzGRX1BY9bkyHFFaNstiWKUbnXvOxT_2cidM,1891
83
83
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
84
84
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
85
85
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -1302,11 +1302,11 @@ vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=1_OXD
1302
1302
  vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=MdrAKN8QGPk_JnNjbEBaVVKwVLPE2judbBcWuYJgbkY,4964
1303
1303
  vellum/workflows/nodes/displayable/bases/search_node.py,sha256=S7J8tTW681O4wcWYerGOfH6h-_BlE8-JMJHpW8eCVG0,3564
1304
1304
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1305
- vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=uwT_sn-XT0uYe7E-0DcJfcb3X33pwE0sw-2ri7FhoTo,8073
1305
+ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=OHU9SnB0JDTTSFIp7io-9Jckelqtq84AP-QOcZBRNXY,8640
1306
1306
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1307
1307
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1308
1308
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1309
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=2Kr7fKtjc1fW5z_6z6noKfWoETIVJbYi0AGhhSw-hsU,3376
1309
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=s93M_EnU-4n60iSKv3FCf0kppwzFH5FNi9o9E58fQ3I,7510
1310
1310
  vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=LfI3kj2zQz6UGMld_uA9z2LjZobqRcgxQO4jdUWkg7o,376
1311
1311
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1312
1312
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=REFZdEVetXGyOK1RbIN1T6yRblrP0hfyZUls2KfjTKg,1016
@@ -1378,8 +1378,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
1378
1378
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1379
1379
  vellum/workflows/workflows/base.py,sha256=mnI-kZ78yt7u6NFSTUo-tYjDnarP-RJ7uZjwjCn6PCQ,16795
1380
1380
  vellum/workflows/workflows/event_filters.py,sha256=-uQcMB7IpPd-idMku8f2QNVhPXPFWo6FZLlGjRf8rCo,1996
1381
- vellum_ai-0.11.9.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1382
- vellum_ai-0.11.9.dist-info/METADATA,sha256=mbIOikcPuEIU344kVitGc9qdEQCbVQn5_-GrKT7XgdI,5128
1383
- vellum_ai-0.11.9.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
1384
- vellum_ai-0.11.9.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1385
- vellum_ai-0.11.9.dist-info/RECORD,,
1381
+ vellum_ai-0.11.10.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1382
+ vellum_ai-0.11.10.dist-info/METADATA,sha256=mPMns1QolQ74Dw64M1w0SbtZXWFMMpEfK7VBYRrAmj8,5129
1383
+ vellum_ai-0.11.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1384
+ vellum_ai-0.11.10.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1385
+ vellum_ai-0.11.10.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.6.1
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -30,6 +30,15 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
30
30
  def get_target_handle_id(self) -> UUID:
31
31
  return self._get_node_display_uuid("target_handle_id")
32
32
 
33
+ def get_target_handle_id_by_source_node_id(self, source_node_id: UUID) -> UUID:
34
+ """
35
+ In the vast majority of cases, nodes will only have a single target handle and can be retrieved independently
36
+ of the source node. However, in rare cases (such as legacy Merge nodes), this method can be overridden to
37
+ account for the case of retrieving one amongst multiple target handles on a node.
38
+ """
39
+
40
+ return self.get_target_handle_id()
41
+
33
42
  def get_source_handle_id(self, port_displays: Dict[Port, PortDisplay]) -> UUID:
34
43
  default_port = self._node.Ports.default
35
44
  default_port_display = port_displays[default_port]
@@ -30,7 +30,10 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
30
30
  node_id = self.node_id
31
31
 
32
32
  node_file_path = inspect.getfile(node)
33
- code = read_file_from_path(node_filepath=node_file_path, script_filepath=(raise_if_descriptor(node.filepath)))
33
+ code = read_file_from_path(
34
+ node_filepath=node_file_path,
35
+ script_filepath=(raise_if_descriptor(node.filepath)), # type: ignore
36
+ )
34
37
  code_inputs = raise_if_descriptor(node.code_inputs)
35
38
 
36
39
  inputs = [
@@ -12,7 +12,6 @@ from vellum.workflows.references import WorkflowInputReference
12
12
  from vellum.workflows.references.output import OutputReference
13
13
  from vellum.workflows.types.core import JsonArray, JsonObject
14
14
  from vellum.workflows.types.generics import WorkflowType
15
- from vellum_ee.workflows.display.nodes import BaseMergeNodeDisplay
16
15
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
17
16
  from vellum_ee.workflows.display.nodes.types import PortDisplay
18
17
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -369,10 +368,7 @@ class VellumWorkflowDisplay(
369
368
  target_node_id = target_node_display.node_id
370
369
 
371
370
  target_handle_id: UUID
372
- if isinstance(target_node_display, BaseMergeNodeDisplay):
373
- target_handle_id = target_node_display.get_target_handle_id_by_source_node_id(source_node_id)
374
- else:
375
- target_handle_id = target_node_display.get_target_handle_id()
371
+ target_handle_id = target_node_display.get_target_handle_id_by_source_node_id(source_node_id)
376
372
 
377
373
  return self._generate_edge_display_from_source(
378
374
  source_node_id, source_handle_id, target_node_id, target_handle_id, overrides