vellum-ai 0.14.70__py3-none-any.whl → 0.14.71__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.
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.14.70",
21
+ "X-Fern-SDK-Version": "0.14.71",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -5,8 +5,11 @@ from vellum.workflows.references import EnvironmentVariableReference
5
5
 
6
6
  class EnvironmentVariables:
7
7
  @staticmethod
8
- def get(name: str, default: Optional[str] = None) -> EnvironmentVariableReference:
9
- return EnvironmentVariableReference(name=name, default=default)
8
+ def get(name: str, default: Optional[str] = None):
9
+ env_ref = EnvironmentVariableReference(name=name)
10
+ if default is not None:
11
+ return env_ref.coalesce(default)
12
+ return env_ref
10
13
 
11
14
 
12
15
  # Deprecated: Use EnvironmentVariables instead. Will be removed in v0.15.0
@@ -98,7 +98,7 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
98
98
  def run(self) -> Outputs:
99
99
  output_type = self.__class__.get_output_type()
100
100
  code, filepath = self._resolve_code()
101
- if not self.packages and self.runtime == "PYTHON_3_11_6":
101
+ if not self.packages and self.runtime == "PYTHON_3_11_6" and not self._has_secrets_in_code_inputs():
102
102
  logs, result = run_code_inline(code, self.code_inputs, output_type, filepath)
103
103
  return self.Outputs(result=result, log=logs)
104
104
 
@@ -133,6 +133,13 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
133
133
 
134
134
  return self.Outputs(result=code_execution_result.output.value, log=code_execution_result.log)
135
135
 
136
+ def _has_secrets_in_code_inputs(self) -> bool:
137
+ """Check if any code_inputs contain VellumSecret instances that require API execution."""
138
+ for input_value in self.code_inputs.values():
139
+ if isinstance(input_value, VellumSecret):
140
+ return True
141
+ return False
142
+
136
143
  def _compile_code_inputs(self) -> List[CodeExecutorInput]:
137
144
  # TODO: We may want to consolidate with prompt deployment input compilation
138
145
  # https://app.shortcut.com/vellum/story/4117
@@ -1233,3 +1233,56 @@ def main(numbers: List[str]) -> str:
1233
1233
  ],
1234
1234
  request_options=None,
1235
1235
  )
1236
+
1237
+
1238
+ def test_run_node__vellum_secret_forces_api_execution(vellum_client):
1239
+ """Test that CodeExecutionNode with VellumSecretReference forces API execution instead of inline execution."""
1240
+
1241
+ # GIVEN a node that subclasses CodeExecutionNode with VellumSecretReference but no packages
1242
+ # This should normally run inline, but the presence of secrets should force API execution
1243
+ class State(BaseState):
1244
+ pass
1245
+
1246
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, str]):
1247
+ code = """
1248
+ def main(secret: str) -> str:
1249
+ return f"Secret value: {secret}"
1250
+ """
1251
+ runtime = "PYTHON_3_11_6"
1252
+ # Note: No packages specified, which would normally trigger inline execution
1253
+
1254
+ code_inputs = {
1255
+ "secret": VellumSecretReference("MY_SECRET"),
1256
+ }
1257
+
1258
+ # AND we know what the Code Execution Node will respond with
1259
+ mock_code_execution = CodeExecutorResponse(
1260
+ log="",
1261
+ output=StringVellumValue(value="Secret value: my_secret_value"),
1262
+ )
1263
+ vellum_client.execute_code.return_value = mock_code_execution
1264
+
1265
+ # WHEN we run the node
1266
+ node = ExampleCodeExecutionNode(state=State())
1267
+ outputs = node.run()
1268
+
1269
+ # THEN the node should have produced the outputs we expect
1270
+ assert outputs == {"result": "Secret value: my_secret_value", "log": ""}
1271
+
1272
+ # AND we should have invoked the Code via API (not inline) due to the secret
1273
+ vellum_client.execute_code.assert_called_once_with(
1274
+ input_values=[
1275
+ CodeExecutorSecretInput(
1276
+ name="secret",
1277
+ value="MY_SECRET",
1278
+ )
1279
+ ],
1280
+ code="""
1281
+ def main(secret: str) -> str:
1282
+ return f"Secret value: {secret}"
1283
+ """,
1284
+ runtime="PYTHON_3_11_6",
1285
+ output_type="STRING",
1286
+ packages=[],
1287
+ request_options=None,
1288
+ )
@@ -1,6 +1,7 @@
1
1
  import os
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING, Any, Optional
3
3
 
4
+ from vellum.workflows.constants import undefined
4
5
  from vellum.workflows.descriptors.base import BaseDescriptor
5
6
 
6
7
  if TYPE_CHECKING:
@@ -8,16 +9,18 @@ if TYPE_CHECKING:
8
9
 
9
10
 
10
11
  class EnvironmentVariableReference(BaseDescriptor[str]):
11
- def __init__(self, *, name: str, default: Optional[str] = None):
12
+ def __init__(
13
+ self,
14
+ *,
15
+ name: str,
16
+ # DEPRECATED - to be removed in 0.15.0 release
17
+ default: Optional[str] = None,
18
+ ):
12
19
  super().__init__(name=name, types=(str,))
13
- self._default = default
14
20
 
15
- def resolve(self, state: "BaseState") -> str:
21
+ def resolve(self, state: "BaseState") -> Any:
16
22
  env_value = os.environ.get(self.name)
17
23
  if env_value is not None:
18
24
  return env_value
19
25
 
20
- if self._default is not None:
21
- return self._default
22
-
23
- return ""
26
+ return undefined
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.70
3
+ Version: 0.14.71
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -41,14 +41,14 @@ vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=9GtbvSJUNF62
41
41
  vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=YhMsi2TG1zSR8E7IpxzzSncOyVLcvqTuGa3mr4RqHd8,2364
42
42
  vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=zo-nalsuayMqeb2GwR2OB9SFK3y2U5aG-rtwrsjdasQ,3089
43
43
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=IniO5KvO0Rw9zghFg3RFvbXBTv6Zi1iuQhaA1DLazqU,2331
44
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=W_ijZBO9EsiyLM_tKyBkVu3V5h3QDx2JB-Tfs1xb-BE,10955
44
+ vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=1ydd1-SXCuJdPDquMrvV5JXIIhf7eBdvY2dXqDz_r5o,11035
45
45
  vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=f7MeoxgKrdyb1dSJsvdDtZPlp1J2Pa4njPvN3qHVktA,6028
46
46
  vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=uaZ2wcZR1J9C9iI0QWAsgNK9IlcuCz1808oxXmiYsLY,3908
47
47
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=RTP_raQWL8ZKoRKLpxLfpyXzw61TZeTCkTuM1uRLIkI,3274
48
48
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=6xf8MJ684KecKPJrGlCJuJYLPtYImXmqN85Y_6KPjW4,1141
49
49
  vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=cT5qT7Nd2v6rSsIErpSAWaxta7txGOSFOZz2AQYQmWE,3536
50
50
  vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=Gos8F1yKN69GmegDO2q3NlGTamibd4rpuTasSU0mK8c,3281
51
- vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=fkkn36UtjovrXYguAermgB5wm2WYakqBNaIiO0BPirI,9545
51
+ vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=bF07csUFSQlAeOayPPws5pz3tBTp1PdwgHb8WItgXmY,12319
52
52
  vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=Ub6XDdVtVarqoqQrIXpJxNbp3xvz37vwpuf93DhLvX8,2670
53
53
  vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=TdIJWh2l8p4tw7ejRexGOFQKnviirUqie3WYwsrVQ4g,3339
54
54
  vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -59,6 +59,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_note_node.py,sha256=uiMB0cOx
59
59
  vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_deployment_node.py,sha256=G-qJyTNJkpqJiEZ3kCJl86CXJINLeFyf2lM0bQHCCOs,3822
60
60
  vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=TtzUj3Zk3ZhwtXE_WyctCC-CmcLB1RxntyF7u-a3i6I,10077
61
61
  vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=h93ysolmbo2viisyhRnXKHPxiDK0I_dSAbYoHFYIoO4,1953
62
+ vellum_ee/workflows/display/nodes/vellum/tests/test_search_node.py,sha256=KvByxgbUkVyfPIVxTUBUk6a92JiJMi8eReZWxzfPExU,3864
62
63
  vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py,sha256=BUzHJgjdWnPeZxjFjHfDBKnbFjYjnbXPjc-1hne1B2Y,3965
63
64
  vellum_ee/workflows/display/nodes/vellum/tests/test_templating_node.py,sha256=LSk2gx9TpGXbAqKe8dggQW8yJZqj-Cf0EGJFeGGlEcw,3321
64
65
  vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py,sha256=ZsLIGnJ9QKrXjYeDW8LEN8M9FnWRQ9TohHFyB6144HY,7970
@@ -83,12 +84,12 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_stat
83
84
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=MNnQ51ZWOQGVfBdpIqvr4OZF0tWdfrh2bsHP3xkTwQw,5841
84
85
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=kLOnUNn-r1w1JXNQcVKe-Vp-fKhSfuDBuDqrjGkFZ3U,5544
85
86
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=v07cILUzS5iFYDrSOAXK93yz50-FtxLaMYMwoaPOv20,7374
86
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=Jcuzu_zjS4ATrPg-pKvrU9WeUFAWMmPQVIcYltG6NfA,14930
87
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=glOPy9y_GOLGMxc7glfo7IGq5iuCDbz9-TE_6Krha2U,18426
87
88
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=1ND_6lfPNUQjSc72k_sxydeeOU5f_kHkRXO65boYFok,21631
88
89
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=3gZuNM8sT6ovVaeoAvd2JoyKwuxokvowlhH8kwDUoZ8,16559
89
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=IIJt7YZBzkhNtbmaMwCX4ENs58QtSIIoBHlMR6OwGU8,8342
90
91
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py,sha256=QXiRjwtiTPeMUl40Pvh_geeU99C3mv1aVS85oeIUwY4,21052
91
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py,sha256=K0mECBUyTNX4I468goeaRf93N7Qfizoc1ctCH5_nNys,12936
92
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py,sha256=JuosR1Xvyy2-0UKuCxx0P_3QH3OXgU88llxpyQ7NScM,12936
92
93
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=KkYZc_bZuq1lmDcvUz3QxIqJLpQPCZioD1FHUNsMJY8,11211
93
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=aZaqRDrkO3ytcmdM2eKJqHSt60MF070NMj6M2vgzOKc,7711
94
95
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=r748dpS13HtwY7t_KQFExFssxRy0xI2d-wxmhiUHRe0,3850
@@ -96,11 +97,12 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling
96
97
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=Cx3oY6vPVap0xm_mChqfQw4zzR4pqV36o_SyD8g6jPY,8727
97
98
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=EL5kfakuoEcwD85dGjhMta-J-PpCHRSDoc80SdbBrQk,2769
98
99
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=RmFUDx8dYdfsOE2CGLvdXqNNRtLLpVzXDN8dqZyMcZ8,5822
100
+ vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
99
101
  vellum_ee/workflows/display/types.py,sha256=i4T7ElU5b5h-nA1i3scmEhO1BqmNDc4eJDHavATD88w,2821
100
102
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
103
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=R_BLSZjdanj3UIR4cS6WVT6ek0i7AKJyHSjK1wPiois,3877
102
104
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
103
- vellum_ee/workflows/display/utils/expressions.py,sha256=Ef1KfY7SmM2mvBDDag2fnU_55Ei_uz5EPDbYPQkI7Zo,13755
105
+ vellum_ee/workflows/display/utils/expressions.py,sha256=bSZ-sRByLCsut8XcRkCFbbqYvw9p7tlIeF-HFnEm664,14354
104
106
  vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3Vd15ijtSN0V97bpW8,1513
105
107
  vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
108
  vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
@@ -140,7 +142,7 @@ vellum/client/README.md,sha256=CuGUYnaE0Imt0KqQ4sIPaUghCjLHkF3DdEvZWu14-8s,4807
140
142
  vellum/client/__init__.py,sha256=AYopGv2ZRVn3zsU8_km6KOvEHDbXiTPCVuYVI7bWvdA,120166
141
143
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
142
144
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
143
- vellum/client/core/client_wrapper.py,sha256=69WJ9aMFe9t-07plHgGcN8V68mijPCXTtGsp7QmE6I4,1869
145
+ vellum/client/core/client_wrapper.py,sha256=3ZqkR_DngkaI-fY5PAgWW2c7RZ88oONDwDkWfxAKLBc,1869
144
146
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
145
147
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
146
148
  vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
@@ -1504,7 +1506,7 @@ vellum/workflows/edges/edge.py,sha256=N0SnY3gKVuxImPAdCbPMPlHJIXbkQ3fwq_LbJRvVMF
1504
1506
  vellum/workflows/emitters/__init__.py,sha256=YyOgaoLtVW8eFNEWODzCYb0HzL0PoSeNRf4diJ1Y0dk,80
1505
1507
  vellum/workflows/emitters/base.py,sha256=D5SADKIvnbgKwIBgYm77jaqvpo1o0rz4MmuX_muRqQU,359
1506
1508
  vellum/workflows/environment/__init__.py,sha256=TJz0m9dwIs6YOwCTeuN0HHsU-ecyjc1OJXx4AFy83EQ,121
1507
- vellum/workflows/environment/environment.py,sha256=7BThIBSRKDkBebgGTYJiPZ_Bx7c8mUigZUypyqF7inM,417
1509
+ vellum/workflows/environment/environment.py,sha256=Ck3RPKXJvtMGx_toqYQQQF-ZwXm5ijVwJpEPTeIJ4_Q,471
1508
1510
  vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
1509
1511
  vellum/workflows/errors/types.py,sha256=nUWuniEfrhdtb-_2GzoDGlYnSJ_yuNUGjVkaKLNr-rM,4049
1510
1512
  vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyuUK48Qw,759
@@ -1608,11 +1610,11 @@ vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMD
1608
1610
  vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
1609
1611
  vellum/workflows/nodes/displayable/bases/utils.py,sha256=ckMUenSsNkiYmSw6FmjSMHYaCk8Y8_sUjL6lkFFEqts,5412
1610
1612
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1611
- vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=o2hP4tWad3-qXY_QtodtGiOGrO8HjeQnqYrxxpxeJVo,9954
1613
+ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=Qh4SPafzdRKbVKQH3h9SgH4vKeQQjvHAfP8ttUxLA2M,10304
1612
1614
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1613
1615
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1614
1616
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1615
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=DRd-nOC04RGjXz-ctxADVq7b-hgdiFN3ZfhK6Ld7J8I,36634
1617
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=47W5fPLpDcaKhCVuauKOwcivx1NfhpbsRMPrs-WBnlY,38441
1616
1618
  vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=VRTKms59vrSR9mDk99cojParZVAP4lzjEeDwDNXU1tk,3837
1617
1619
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1618
1620
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=71ZUNfTiD7t2Kai2ypw0tmv1lSf1brQaHAQD-SeUrGE,1101
@@ -1672,7 +1674,7 @@ vellum/workflows/ports/port.py,sha256=j_qiZlpx-a1cK5E7sxXwPcb_9NS-KUM-JoP8mgqg32
1672
1674
  vellum/workflows/ports/utils.py,sha256=cWJ9xX1KrHBTiU3xe6t7Rs0yaOy9RV18GMtHaAshAsc,3094
1673
1675
  vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcvEcsUBrzLIs,591
1674
1676
  vellum/workflows/references/constant.py,sha256=6yUT4q1sMj1hkI_tzzQ9AYcmeeDYFUNCqUq_W2DN0S8,540
1675
- vellum/workflows/references/environment_variable.py,sha256=-RGql8dSrhimfCdrT_G_LSYz6VlKpfAf-N7XEyvgbBk,649
1677
+ vellum/workflows/references/environment_variable.py,sha256=evpxiBKzBk74JzxBXWkfTrZe4Kx-fuUG1W6XjtXzaUw,700
1676
1678
  vellum/workflows/references/execution_count.py,sha256=JILHqt8ELdc9ct-WsVCA5X-rKiP1rmJODw-XTf4kpHI,722
1677
1679
  vellum/workflows/references/external_input.py,sha256=c_4SojTpykCSbGS1Pjmx9FfquyYGMPksoj0AbrWv7Go,2064
1678
1680
  vellum/workflows/references/input.py,sha256=3INu-TLTi4dziWmva6LO3WvgDlPzsjayUx61cVvqLJA,325
@@ -1724,8 +1726,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1724
1726
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1725
1727
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=fROqff6AZpCIzaSwOKSdtYy4XR0UZQ6ejxL3RJOSJVs,20447
1726
1728
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1727
- vellum_ai-0.14.70.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1728
- vellum_ai-0.14.70.dist-info/METADATA,sha256=qt_owPpRPSEfBhHgLJGnUPlKvtDCCmMBkMLu1UcwUfQ,5556
1729
- vellum_ai-0.14.70.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1730
- vellum_ai-0.14.70.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1731
- vellum_ai-0.14.70.dist-info/RECORD,,
1729
+ vellum_ai-0.14.71.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1730
+ vellum_ai-0.14.71.dist-info/METADATA,sha256=C5GX4tfVPU2-YXu_9tu4RToI1a6SafllfrsjW6SiM9c,5556
1731
+ vellum_ai-0.14.71.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1732
+ vellum_ai-0.14.71.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1733
+ vellum_ai-0.14.71.dist-info/RECORD,,
@@ -2,6 +2,7 @@ from uuid import UUID
2
2
  from typing import Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union
3
3
 
4
4
  from vellum import FunctionDefinition, PromptBlock, RichTextChildBlock, VellumVariable
5
+ from vellum.workflows.descriptors.base import BaseDescriptor
5
6
  from vellum.workflows.nodes import InlinePromptNode
6
7
  from vellum.workflows.types.core import JsonObject
7
8
  from vellum.workflows.utils.functions import compile_function_definition
@@ -18,9 +19,10 @@ _InlinePromptNodeType = TypeVar("_InlinePromptNodeType", bound=InlinePromptNode)
18
19
 
19
20
 
20
21
  class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generic[_InlinePromptNodeType]):
21
- __serializable_inputs__ = {InlinePromptNode.prompt_inputs, InlinePromptNode.functions}
22
+ __serializable_inputs__ = {
23
+ InlinePromptNode.prompt_inputs,
24
+ }
22
25
  __unserializable_attributes__ = {
23
- InlinePromptNode.blocks,
24
26
  InlinePromptNode.parameters,
25
27
  InlinePromptNode.settings,
26
28
  InlinePromptNode.expand_meta,
@@ -45,7 +47,9 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
45
47
  ml_model = str(raise_if_descriptor(node.ml_model))
46
48
 
47
49
  blocks: list = [
48
- self._generate_prompt_block(block, input_variable_id_by_name, [i]) for i, block in enumerate(node_blocks)
50
+ self._generate_prompt_block(block, input_variable_id_by_name, [i])
51
+ for i, block in enumerate(node_blocks)
52
+ if not isinstance(block, BaseDescriptor)
49
53
  ]
50
54
 
51
55
  functions = (
@@ -7,6 +7,7 @@ from vellum import (
7
7
  VellumValueLogicalConditionGroupRequest,
8
8
  VellumValueLogicalConditionRequest,
9
9
  )
10
+ from vellum.workflows.nodes.displayable.bases.types import MetadataLogicalCondition, MetadataLogicalConditionGroup
10
11
  from vellum.workflows.nodes.displayable.search_node import SearchNode
11
12
  from vellum.workflows.references import OutputReference
12
13
  from vellum.workflows.types.core import JsonArray, JsonObject
@@ -88,7 +89,8 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
88
89
  node_inputs: Dict[str, NodeInput] = {}
89
90
 
90
91
  options = raise_if_descriptor(node.options)
91
- filters = options.filters if options else None
92
+ raw_filters = raise_if_descriptor(node.filters)
93
+ filters = raw_filters if raw_filters else options.filters if options else None
92
94
 
93
95
  external_id_filters = filters.external_ids if filters else None
94
96
 
@@ -104,17 +106,21 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
104
106
  raw_metadata_filters, display_context=display_context
105
107
  )
106
108
 
107
- result_merging = options.result_merging if options else None
109
+ raw_result_merging = raise_if_descriptor(node.result_merging)
110
+ result_merging = raw_result_merging if raw_result_merging else options.result_merging if options else None
108
111
  result_merging_enabled = True if result_merging and result_merging.enabled else False
109
112
 
110
113
  raw_weights = raise_if_descriptor(node.weights)
111
114
  weights = raw_weights if raw_weights is not None else options.weights if options is not None else None
112
115
 
116
+ raw_limit = raise_if_descriptor(node.limit)
117
+ limit = raw_limit if raw_limit is not None else options.limit if options is not None else None
118
+
113
119
  node_input_names_and_values = [
114
120
  ("query", node.query),
115
121
  ("document_index_id", node.document_index),
116
122
  ("weights", weights.dict() if weights else None),
117
- ("limit", options.limit if options else None),
123
+ ("limit", limit),
118
124
  ("separator", raise_if_descriptor(node.chunk_separator)),
119
125
  (
120
126
  "result_merging_enabled",
@@ -141,7 +147,12 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
141
147
 
142
148
  def _serialize_logical_expression(
143
149
  self,
144
- logical_expression: Union[VellumValueLogicalConditionGroupRequest, VellumValueLogicalConditionRequest],
150
+ logical_expression: Union[
151
+ VellumValueLogicalConditionGroupRequest,
152
+ VellumValueLogicalConditionRequest,
153
+ MetadataLogicalConditionGroup,
154
+ MetadataLogicalCondition,
155
+ ],
145
156
  display_context: WorkflowDisplayContext,
146
157
  path: List[int] = [],
147
158
  ) -> Tuple[JsonObject, List[NodeInput]]:
@@ -175,10 +186,10 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
175
186
 
176
187
  lhs_query_input_id: UUID = self.metadata_filter_input_id_by_operand_id.get(
177
188
  UUID(lhs_variable_id)
178
- ) or uuid4_from_hash(f"{self.node_id}|{hash(tuple(path))}")
189
+ ) or uuid4_from_hash(f"{self.node_id}|lhs|{hash(tuple(path))}")
179
190
  rhs_query_input_id: UUID = self.metadata_filter_input_id_by_operand_id.get(
180
191
  UUID(rhs_variable_id)
181
- ) or uuid4_from_hash(f"{self.node_id}|{hash(tuple(path))}")
192
+ ) or uuid4_from_hash(f"{self.node_id}|rhs|{hash(tuple(path))}")
182
193
 
183
194
  return (
184
195
  {
@@ -206,5 +217,57 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
206
217
  ),
207
218
  ],
208
219
  )
220
+
221
+ elif isinstance(logical_expression, MetadataLogicalConditionGroup):
222
+ conditions = []
223
+ variables = []
224
+ for idx, metadata_condition in enumerate(logical_expression.conditions):
225
+ serialized_condition, serialized_variables = self._serialize_logical_expression(
226
+ metadata_condition, display_context=display_context, path=path + [idx]
227
+ )
228
+ conditions.append(serialized_condition)
229
+ variables.extend(serialized_variables)
230
+
231
+ return (
232
+ {
233
+ "type": "LOGICAL_CONDITION_GROUP",
234
+ "combinator": logical_expression.combinator,
235
+ "conditions": conditions,
236
+ "negated": logical_expression.negated,
237
+ },
238
+ variables,
239
+ )
240
+
241
+ elif isinstance(logical_expression, MetadataLogicalCondition):
242
+ lhs_variable = logical_expression.lhs_variable
243
+ rhs_variable = logical_expression.rhs_variable
244
+
245
+ lhs_query_input_id = uuid4_from_hash(f"{self.node_id}|lhs|{hash(tuple(path))}")
246
+ rhs_query_input_id = uuid4_from_hash(f"{self.node_id}|rhs|{hash(tuple(path))}")
247
+
248
+ return (
249
+ {
250
+ "type": "LOGICAL_CONDITION",
251
+ "lhs_variable_id": str(lhs_query_input_id),
252
+ "operator": logical_expression.operator,
253
+ "rhs_variable_id": str(rhs_query_input_id),
254
+ },
255
+ [
256
+ create_node_input(
257
+ self.node_id,
258
+ f"vellum-query-builder-variable-{lhs_query_input_id}",
259
+ lhs_variable,
260
+ display_context,
261
+ input_id=lhs_query_input_id,
262
+ ),
263
+ create_node_input(
264
+ self.node_id,
265
+ f"vellum-query-builder-variable-{rhs_query_input_id}",
266
+ rhs_variable,
267
+ display_context,
268
+ input_id=rhs_query_input_id,
269
+ ),
270
+ ],
271
+ )
209
272
  else:
210
273
  raise ValueError(f"Unsupported logical expression type: {type(logical_expression)}")
@@ -0,0 +1,104 @@
1
+ from vellum.workflows import BaseWorkflow
2
+ from vellum.workflows.inputs import BaseInputs
3
+ from vellum.workflows.nodes.displayable import SearchNode
4
+ from vellum.workflows.nodes.displayable.bases.types import (
5
+ MetadataLogicalCondition,
6
+ MetadataLogicalConditionGroup,
7
+ SearchFilters,
8
+ )
9
+ from vellum.workflows.state.base import BaseState
10
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
11
+
12
+
13
+ def test_search_filters_with_input_reference():
14
+ """Test that SearchFilters with MetadataLogicalCondition using input references can be serialized"""
15
+
16
+ # GIVEN a search node with a metadata filter that uses an input reference
17
+ class TestInputs(BaseInputs):
18
+ file_id: str
19
+
20
+ class MySearchNode(SearchNode):
21
+ query = "my query"
22
+ document_index = "document_index"
23
+ filters = SearchFilters(
24
+ external_ids=None,
25
+ metadata=MetadataLogicalConditionGroup(
26
+ combinator="AND",
27
+ negated=False,
28
+ conditions=[MetadataLogicalCondition(lhs_variable="ID", operator="=", rhs_variable=TestInputs.file_id)],
29
+ ),
30
+ )
31
+
32
+ # AND a workflow with the Search Node
33
+ class Workflow(BaseWorkflow[TestInputs, BaseState]):
34
+ graph = MySearchNode
35
+
36
+ # WHEN the workflow is serialized
37
+ workflow_display = get_workflow_display(workflow_class=Workflow)
38
+ serialized_workflow: dict = workflow_display.serialize()
39
+
40
+ # THEN the node should properly serialize the filter reference
41
+ serialized_search_node = next(
42
+ node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["id"] == str(MySearchNode.__id__)
43
+ )
44
+
45
+ serialized_metadata_filter = next(
46
+ inp for inp in serialized_search_node["inputs"] if inp["key"] == "metadata_filters"
47
+ )
48
+
49
+ assert serialized_metadata_filter == {
50
+ "id": "4a9f96aa-ba3b-4c4e-9ce4-370fe64f717f",
51
+ "key": "metadata_filters",
52
+ "value": {
53
+ "combinator": "OR",
54
+ "rules": [
55
+ {
56
+ "data": {
57
+ "type": "JSON",
58
+ "value": {
59
+ "combinator": "AND",
60
+ "conditions": [
61
+ {
62
+ "lhs_variable_id": "9aedaffa-c2a4-4c37-9969-184e1ff43ded",
63
+ "operator": "=",
64
+ "rhs_variable_id": "c2151ef1-ad98-4940-b0e9-28dabe47a951",
65
+ "type": "LOGICAL_CONDITION",
66
+ }
67
+ ],
68
+ "negated": False,
69
+ "type": "LOGICAL_CONDITION_GROUP",
70
+ },
71
+ },
72
+ "type": "CONSTANT_VALUE",
73
+ }
74
+ ],
75
+ },
76
+ }
77
+
78
+ # AND the LHS filter references should be present as node inputs
79
+ serialized_lhs_input = next(
80
+ inp for inp in serialized_search_node["inputs"] if inp["id"] == "9aedaffa-c2a4-4c37-9969-184e1ff43ded"
81
+ )
82
+ assert serialized_lhs_input["value"] == {
83
+ "combinator": "OR",
84
+ "rules": [
85
+ {
86
+ "data": {"type": "STRING", "value": "ID"},
87
+ "type": "CONSTANT_VALUE",
88
+ }
89
+ ],
90
+ }
91
+
92
+ # AND the RHS filter references should be present as node inputs
93
+ serialized_rhs_input = next(
94
+ inp for inp in serialized_search_node["inputs"] if inp["id"] == "c2151ef1-ad98-4940-b0e9-28dabe47a951"
95
+ )
96
+ assert serialized_rhs_input["value"] == {
97
+ "combinator": "OR",
98
+ "rules": [
99
+ {
100
+ "data": {"input_variable_id": "e2f4fff9-1277-47cb-8988-12f8ada450ba"},
101
+ "type": "INPUT_VARIABLE",
102
+ }
103
+ ],
104
+ }
@@ -174,6 +174,34 @@ def test_serialize_workflow():
174
174
  "name": "ml_model",
175
175
  "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "gpt-4o"}},
176
176
  },
177
+ {
178
+ "id": "25f935f3-363f-4ead-a5a0-db234ca67e1e",
179
+ "name": "blocks",
180
+ "value": {
181
+ "type": "CONSTANT_VALUE",
182
+ "value": {
183
+ "type": "JSON",
184
+ "value": [
185
+ {
186
+ "block_type": "CHAT_MESSAGE",
187
+ "state": None,
188
+ "cache_config": None,
189
+ "chat_role": "SYSTEM",
190
+ "chat_source": None,
191
+ "chat_message_unterminated": None,
192
+ "blocks": [
193
+ {
194
+ "block_type": "JINJA",
195
+ "state": None,
196
+ "cache_config": None,
197
+ "template": "What's your favorite {{noun}}?",
198
+ }
199
+ ],
200
+ }
201
+ ],
202
+ },
203
+ },
204
+ },
177
205
  {
178
206
  "id": "ffabe7d2-8ab6-4201-9d41-c4d7be1386e1",
179
207
  "name": "prompt_inputs",
@@ -355,3 +383,57 @@ def test_serialize_workflow_with_descriptor_functions():
355
383
  "node_output_id": "470fadb9-b8b5-477e-a502-5209d398bcf9",
356
384
  "type": "NODE_OUTPUT",
357
385
  }
386
+
387
+
388
+ def test_serialize_workflow_with_descriptor_blocks():
389
+ """Test that serialization handles BaseDescriptor instances in blocks list."""
390
+
391
+ class TestInputs(BaseInputs):
392
+ noun: str
393
+
394
+ class UpstreamNode(BaseNode):
395
+ class Outputs(BaseNode.Outputs):
396
+ results: list
397
+
398
+ def run(self) -> Outputs:
399
+ return self.Outputs(results=["test"])
400
+
401
+ class TestInlinePromptNodeWithDescriptorBlocks(InlinePromptNode):
402
+ ml_model = "gpt-4o"
403
+ blocks = [UpstreamNode.Outputs.results[0]] # type: ignore
404
+ prompt_inputs = {"noun": TestInputs.noun}
405
+
406
+ class TestWorkflow(BaseWorkflow[TestInputs, BaseState]):
407
+ graph = UpstreamNode >> TestInlinePromptNodeWithDescriptorBlocks
408
+
409
+ workflow_display = get_workflow_display(workflow_class=TestWorkflow)
410
+ serialized: dict = workflow_display.serialize()
411
+
412
+ prompt_nodes = [node for node in serialized["workflow_raw_data"]["nodes"] if node["type"] == "PROMPT"]
413
+ prompt_node = prompt_nodes[0]
414
+
415
+ blocks = prompt_node["data"]["exec_config"]["prompt_template_block_data"]["blocks"]
416
+ descriptor_blocks = [block for block in blocks if not isinstance(block, dict) or not block.get("block_type")]
417
+ assert len(descriptor_blocks) == 0, "BaseDescriptor blocks should not appear in serialized blocks"
418
+
419
+ blocks_attr = next((attr for attr in prompt_node["attributes"] if attr["name"] == "blocks"), None)
420
+ assert blocks_attr is not None, "blocks attribute should be present when blocks contain BaseDescriptor"
421
+ assert blocks_attr["value"]["type"] == "ARRAY_REFERENCE", "blocks attribute should be serialized as ARRAY_REFERENCE"
422
+ assert blocks_attr["value"]["items"] == [
423
+ {
424
+ "type": "BINARY_EXPRESSION",
425
+ "lhs": {
426
+ "type": "NODE_OUTPUT",
427
+ "node_id": str(UpstreamNode.__id__),
428
+ "node_output_id": str(UpstreamNode.__output_ids__["results"]),
429
+ },
430
+ "operator": "accessField",
431
+ "rhs": {
432
+ "type": "CONSTANT_VALUE",
433
+ "value": {
434
+ "type": "NUMBER",
435
+ "value": 0.0,
436
+ },
437
+ },
438
+ }
439
+ ]
@@ -167,7 +167,7 @@ def test_serialize_workflow():
167
167
  "rules": [
168
168
  {
169
169
  "type": "INPUT_VARIABLE",
170
- "data": {"input_variable_id": "8ffffeb2-79b3-4105-acc7-78b0267da955"},
170
+ "data": {"input_variable_id": "b118247f-96dd-4b3e-8289-9f277483c520"},
171
171
  }
172
172
  ],
173
173
  "combinator": "OR",
@@ -180,7 +180,7 @@ def test_serialize_workflow():
180
180
  "rules": [
181
181
  {
182
182
  "type": "INPUT_VARIABLE",
183
- "data": {"input_variable_id": "8ffffeb2-79b3-4105-acc7-78b0267da955"},
183
+ "data": {"input_variable_id": "aae2c10a-88b7-40bd-87a2-5e1e60c1e906"},
184
184
  }
185
185
  ],
186
186
  "combinator": "OR",
@@ -193,7 +193,7 @@ def test_serialize_workflow():
193
193
  "rules": [
194
194
  {
195
195
  "type": "INPUT_VARIABLE",
196
- "data": {"input_variable_id": "f5eee974-b0c3-4775-bc8a-679a9e99d7ba"},
196
+ "data": {"input_variable_id": "c9611a62-d1f5-4b41-bf9c-1aa3355760b4"},
197
197
  }
198
198
  ],
199
199
  "combinator": "OR",
@@ -206,7 +206,7 @@ def test_serialize_workflow():
206
206
  "rules": [
207
207
  {
208
208
  "type": "INPUT_VARIABLE",
209
- "data": {"input_variable_id": "f5eee974-b0c3-4775-bc8a-679a9e99d7ba"},
209
+ "data": {"input_variable_id": "f374640e-a5c0-470e-ac71-c36c2b198c00"},
210
210
  }
211
211
  ],
212
212
  "combinator": "OR",
@@ -0,0 +1,37 @@
1
+ import pytest
2
+
3
+ from vellum.workflows import BaseWorkflow
4
+ from vellum.workflows.inputs.base import BaseInputs
5
+ from vellum.workflows.nodes.bases.base import BaseNode
6
+ from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
7
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
+
9
+
10
+ def test_workflow_serialization_error_when_node_references_unparameterized_inputs():
11
+ """Test that a helpful error is raised when a node references inputs not parameterized by the workflow."""
12
+
13
+ class CustomInputs(BaseInputs):
14
+ custom_input: str
15
+
16
+ class TestNode(BaseNode):
17
+ class Outputs(BaseNode.Outputs):
18
+ output = CustomInputs.custom_input
19
+
20
+ class TestWorkflow(BaseWorkflow):
21
+ graph = TestNode
22
+
23
+ class Outputs(BaseWorkflow.Outputs):
24
+ result = TestNode.Outputs.output
25
+
26
+ workflow_display = get_workflow_display(workflow_class=TestWorkflow)
27
+
28
+ with pytest.raises(UnsupportedSerializationException) as exc_info:
29
+ workflow_display.serialize()
30
+
31
+ error_message = str(exc_info.value)
32
+ expected_message = (
33
+ "Inputs class 'CustomInputs' referenced during serialization of 'TestWorkflow' "
34
+ "without parameterizing this Workflow with this Inputs definition. "
35
+ "Update your Workflow definition to 'TestWorkflow(BaseWorkflow[CustomInputs, BaseState])'."
36
+ )
37
+ assert error_message == expected_message
@@ -211,7 +211,16 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
211
211
  return serialize_value(display_context, child_descriptor)
212
212
 
213
213
  if isinstance(value, WorkflowInputReference):
214
- workflow_input_display = display_context.global_workflow_input_displays[value]
214
+ try:
215
+ workflow_input_display = display_context.global_workflow_input_displays[value]
216
+ except KeyError:
217
+ inputs_class_name = value.inputs_class.__name__
218
+ workflow_class_name = display_context.workflow_display_class.infer_workflow_class().__name__
219
+ raise UnsupportedSerializationException(
220
+ f"Inputs class '{inputs_class_name}' referenced during serialization of '{workflow_class_name}' "
221
+ f"without parameterizing this Workflow with this Inputs definition. Update your Workflow "
222
+ f"definition to '{workflow_class_name}(BaseWorkflow[{inputs_class_name}, BaseState])'."
223
+ )
215
224
  return {
216
225
  "type": "WORKFLOW_INPUT",
217
226
  "input_variable_id": str(workflow_input_display.id),