vellum-ai 0.14.76__py3-none-any.whl → 0.14.78__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.76",
21
+ "X-Fern-SDK-Version": "0.14.78",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -71,7 +71,10 @@ class OnValidatePython(ValidatePythonHandlerProtocol):
71
71
  self.tracked_descriptors[key] = value
72
72
  # TODO: This does not yet work for descriptors that map to more complex types
73
73
  # https://app.shortcut.com/vellum/story/4636
74
- input[key] = value.types[0]()
74
+ if len(value.types) == 0:
75
+ input[key] = dict()
76
+ else:
77
+ input[key] = value.types[0]()
75
78
 
76
79
  def on_success(self, result: Any) -> None:
77
80
  if self.tracked_descriptors:
File without changes
@@ -0,0 +1,30 @@
1
+ from typing import Any
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from vellum.workflows.expressions.accessor import AccessorExpression
6
+ from vellum.workflows.references.constant import ConstantValueReference
7
+ from vellum.workflows.state.base import BaseState
8
+
9
+
10
+ class TestState(BaseState):
11
+ pass
12
+
13
+
14
+ class TestModel(BaseModel):
15
+ field: Any
16
+
17
+
18
+ def test_pydantic_plugin__empty_types_descriptor():
19
+ """
20
+ Test that the pydantic plugin handles BaseDescriptor with empty types gracefully.
21
+ """
22
+
23
+ base_ref = ConstantValueReference({"name": "test"})
24
+ accessor = AccessorExpression(base=base_ref, field="name")
25
+
26
+ model = TestModel(field=accessor)
27
+
28
+ assert model is not None
29
+ assert hasattr(model, "field")
30
+ assert model.field == accessor
@@ -0,0 +1,3 @@
1
+ from .blocks import TextSystemMessage, TextUserMessage
2
+
3
+ __all__ = ["TextSystemMessage", "TextUserMessage"]
@@ -0,0 +1,3 @@
1
+ from .helpers import TextSystemMessage, TextUserMessage
2
+
3
+ __all__ = ["TextSystemMessage", "TextUserMessage"]
@@ -0,0 +1,31 @@
1
+ from vellum import ChatMessagePromptBlock, PlainTextPromptBlock, RichTextPromptBlock
2
+
3
+
4
+ def TextSystemMessage(content: str) -> ChatMessagePromptBlock:
5
+ """
6
+ Create a text system message that autocasts to ChatMessagePromptBlock.
7
+
8
+ Args:
9
+ content: The text content for the system message
10
+
11
+ Returns:
12
+ ChatMessagePromptBlock configured as a system message
13
+ """
14
+ return ChatMessagePromptBlock(
15
+ chat_role="SYSTEM", blocks=[RichTextPromptBlock(blocks=[PlainTextPromptBlock(text=content)])]
16
+ )
17
+
18
+
19
+ def TextUserMessage(content: str) -> ChatMessagePromptBlock:
20
+ """
21
+ Create a text user message that autocasts to ChatMessagePromptBlock.
22
+
23
+ Args:
24
+ content: The text content for the user message
25
+
26
+ Returns:
27
+ ChatMessagePromptBlock configured as a user message
28
+ """
29
+ return ChatMessagePromptBlock(
30
+ chat_role="USER", blocks=[RichTextPromptBlock(blocks=[PlainTextPromptBlock(text=content)])]
31
+ )
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Mapping
2
2
  import dataclasses
3
- from typing import Any, Sequence, Type, TypeVar, Union
3
+ from typing import Any, Sequence, Type, TypeVar, Union, get_args, get_origin
4
4
 
5
5
  from pydantic import BaseModel, GetCoreSchemaHandler
6
6
  from pydantic_core import core_schema
@@ -22,12 +22,41 @@ class AccessorExpression(BaseDescriptor[Any]):
22
22
  ) -> None:
23
23
  super().__init__(
24
24
  name=f"{base.name}.{field}",
25
- types=(),
25
+ types=self._infer_accessor_types(base, field),
26
26
  instance=None,
27
27
  )
28
28
  self._base = base
29
29
  self._field = field
30
30
 
31
+ def _infer_accessor_types(self, base: BaseDescriptor[LHS], field: Union[str, int]) -> tuple[Type, ...]:
32
+ """
33
+ Infer the types for this accessor expression based on the base descriptor's types
34
+ and the field being accessed.
35
+ """
36
+ if not base.types:
37
+ return ()
38
+
39
+ inferred_types = []
40
+
41
+ for base_type in base.types:
42
+ origin = get_origin(base_type)
43
+ args = get_args(base_type)
44
+
45
+ if isinstance(field, int) and origin in (list, tuple) and args:
46
+ if origin is list:
47
+ inferred_types.append(args[0])
48
+ elif origin is tuple and len(args) == 2 and args[1] is ...:
49
+ inferred_types.append(args[0])
50
+ elif origin is tuple and len(args) > abs(field):
51
+ if field >= 0:
52
+ inferred_types.append(args[field])
53
+ else:
54
+ inferred_types.append(args[field])
55
+ elif isinstance(field, str) and origin in (dict,) and len(args) >= 2:
56
+ inferred_types.append(args[1]) # Value type from Dict[K, V]
57
+
58
+ return tuple(set(inferred_types)) if inferred_types else ()
59
+
31
60
  def resolve(self, state: "BaseState") -> Any:
32
61
  base = resolve_value(self._base, state)
33
62
 
@@ -1,11 +1,14 @@
1
1
  import pytest
2
2
  from dataclasses import dataclass
3
+ from typing import Dict, List
3
4
 
4
5
  from pydantic import BaseModel
5
6
 
6
7
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
7
8
  from vellum.workflows.expressions.accessor import AccessorExpression
9
+ from vellum.workflows.outputs.base import BaseOutputs
8
10
  from vellum.workflows.references.constant import ConstantValueReference
11
+ from vellum.workflows.references.output import OutputReference
9
12
  from vellum.workflows.state.base import BaseState
10
13
 
11
14
 
@@ -187,3 +190,59 @@ def test_accessor_expression_unsupported_type():
187
190
  accessor.resolve(state)
188
191
 
189
192
  assert "Cannot get field field from 42" in str(exc_info.value)
193
+
194
+
195
+ def test_accessor_expression_list_type_inference():
196
+ """Test that accessor expressions correctly infer element types from List[str]."""
197
+ base_ref: OutputReference = OutputReference(
198
+ name="test_list",
199
+ types=(List[str],),
200
+ instance=None,
201
+ outputs_class=BaseOutputs,
202
+ )
203
+
204
+ accessor = AccessorExpression(base=base_ref, field=0)
205
+
206
+ assert accessor.types == (str,)
207
+
208
+
209
+ def test_accessor_expression_tuple_type_inference():
210
+ """Test that accessor expressions correctly infer element types from Tuple types."""
211
+ base_ref: OutputReference = OutputReference(
212
+ name="test_tuple",
213
+ types=(tuple,),
214
+ instance=None,
215
+ outputs_class=BaseOutputs,
216
+ )
217
+
218
+ accessor = AccessorExpression(base=base_ref, field=0)
219
+
220
+ assert accessor.types == ()
221
+
222
+
223
+ def test_accessor_expression_dict_type_inference():
224
+ """Test that accessor expressions correctly infer value types from Dict types."""
225
+ base_ref: OutputReference = OutputReference(
226
+ name="test_dict",
227
+ types=(Dict[str, int],),
228
+ instance=None,
229
+ outputs_class=BaseOutputs,
230
+ )
231
+
232
+ accessor = AccessorExpression(base=base_ref, field="some_key")
233
+
234
+ assert accessor.types == (int,)
235
+
236
+
237
+ def test_accessor_expression_empty_base_types():
238
+ """Test that accessor expressions handle empty base types gracefully."""
239
+ base_ref: OutputReference = OutputReference(
240
+ name="test_empty",
241
+ types=(),
242
+ instance=None,
243
+ outputs_class=BaseOutputs,
244
+ )
245
+
246
+ accessor = AccessorExpression(base=base_ref, field=0)
247
+
248
+ assert accessor.types == ()
@@ -251,7 +251,12 @@ def create_function_node(
251
251
  )
252
252
  else:
253
253
  # For regular functions, use CodeExecutionNode approach
254
- function_source = inspect.getsource(function)
254
+ # function tool must be put in another file (no need to have the same name)
255
+ source_path = inspect.getmodule(function)
256
+ if source_path is not None:
257
+ function_source = inspect.getsource(source_path)
258
+ else:
259
+ function_source = f"<source code not available for {function.__name__}>"
255
260
  function_name = function.__name__
256
261
 
257
262
  code = f'''
@@ -10,6 +10,7 @@ from typing import Any, Callable, Dict, Type
10
10
  from pydantic import BaseModel
11
11
 
12
12
  from vellum.workflows.constants import undefined
13
+ from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
13
14
  from vellum.workflows.inputs.base import BaseInputs
14
15
  from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
15
16
  from vellum.workflows.ports.port import Port
@@ -21,6 +22,10 @@ class DefaultStateEncoder(JSONEncoder):
21
22
  encoders: Dict[Type, Callable] = {}
22
23
 
23
24
  def default(self, obj: Any) -> Any:
25
+ if isinstance(obj, CoalesceExpression):
26
+ empty_state = BaseState()
27
+ return self.default(obj.resolve(empty_state))
28
+
24
29
  if isinstance(obj, BaseState):
25
30
  return dict(obj)
26
31
 
@@ -61,9 +66,11 @@ class DefaultStateEncoder(JSONEncoder):
61
66
 
62
67
  if callable(obj):
63
68
  function_definition = compile_function_definition(obj)
64
- try:
65
- source_code = inspect.getsource(obj)
66
- except Exception:
69
+ source_path = inspect.getsourcefile(obj)
70
+ if source_path is not None:
71
+ with open(source_path, "r") as f:
72
+ source_code = f.read()
73
+ else:
67
74
  source_code = f"<source code not available for {obj.__name__}>"
68
75
 
69
76
  return {
@@ -50,6 +50,11 @@ def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -
50
50
  # Number now supports float and int
51
51
  elif types == [float, int]:
52
52
  return "NUMBER"
53
+
54
+ collapse_types = _collapse_types(types)
55
+ if len(collapse_types) == 1:
56
+ return primitive_type_to_vellum_variable_type(collapse_types[0])
57
+
53
58
  raise ValueError(f"Expected Descriptor to only have one type, got {types}")
54
59
 
55
60
  type_ = type_.types[0]
@@ -129,3 +134,35 @@ def _is_type_optionally_equal(type_: Type, target_type: Type) -> bool:
129
134
 
130
135
  def _is_type_optionally_in(type_: Type, target_types: Tuple[Type, ...]) -> bool:
131
136
  return any(_is_type_optionally_equal(type_, target_type) for target_type in target_types)
137
+
138
+
139
+ def _collapse_types(types: List[Type]) -> List[Type]:
140
+ """
141
+ Collapses a list of types into a list of types that are not subtypes of each other.
142
+ What remains are the "most specific" types.
143
+ """
144
+
145
+ new_types: List[Type] = []
146
+ for target_type in types:
147
+ if any(_is_subtype(source_type, target_type) for source_type in new_types):
148
+ continue
149
+
150
+ new_types.append(target_type)
151
+
152
+ return new_types
153
+
154
+
155
+ def _is_subtype(source_type: Type, target_type: Type) -> bool:
156
+ """
157
+ Checks if `source_type` is a strict subtype of `target_type`. Meaning all values that are
158
+ of type `source_type` are also of type `target_type`.
159
+ """
160
+
161
+ if source_type == target_type:
162
+ return True
163
+
164
+ source_origin = get_origin(source_type)
165
+ if source_origin == target_type:
166
+ return True
167
+
168
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.76
3
+ Version: 0.14.78
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -84,7 +84,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_stat
84
84
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=w1BYEZXIMjWmcKGQXzhelHnKFlEAXmsgjkI9fcgqKXA,5850
85
85
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=-T0cd2jx1bC0ZNtAESF78fnYD_0nOqo8zMMLwRHUTRM,6227
86
86
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=PZ9Yec8D6e9wM2kh5eWtNvK5BghTf1RdvoSW5_pw3FM,7384
87
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=tljn-ueIMEW3j2q6Ni9O852sdQMjut05ox4paBeJjm8,23714
87
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=vVqxXHieRUPP1Rt4ITnjMxuGU52WBfifoEzPyHQnd7w,25618
88
88
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=3Djct7lIr2TPW-wDd_t6eoNdsE0hBOj9OwKWRroXMUo,21659
89
89
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=91u9FWLErrNGfUkZ22ifk6IazGtaYoDFbZYjhxEESgI,16579
90
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=rtkAQw5awOFX3pWh0qz3f766-tTnPhToGsXVSHO4M4U,8352
@@ -94,8 +94,8 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_
94
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=V8b6gKghLlO7PJI8xeNdnfn8aII0W_IFQvSQBQM62UQ,7721
95
95
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=hDWtKXmGI1CKhTwTNqpu_d5RkE5n7SolMLtgd87KqTI,3856
96
96
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=6q-lTGMp5HbQba3NsHUFSit8_zEBxIVPGE8yCw4SVx0,25720
97
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=36-fpwj53JcRmo7RuHrsS6ZjJ3q4j8BC1wuKruBjIt8,9137
98
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=zlb0Kfg1zqJsz1tD_1O-hkSKhV4-gfYI6AkK4-Yjk7U,2315
97
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=nG5orqTpRwx_jJDaf0Z3rPXe0SD8i4Rrp29GVmKCPBQ,9328
98
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=mova0sPD3evHiHIN1O0VynxlCp-uOcEIKve5Pd_oCDg,4069
99
99
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
100
100
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=J4ouI8KxbMfxQP2Zq_9cWMGYgbjCWmKzjCJEtnSJb0I,5829
101
101
  vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
@@ -103,7 +103,7 @@ vellum_ee/workflows/display/types.py,sha256=i4T7ElU5b5h-nA1i3scmEhO1BqmNDc4eJDHa
103
103
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
104
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
105
105
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
106
- vellum_ee/workflows/display/utils/expressions.py,sha256=KxEl8yNx0yPvy0y84qABef5DwxRYGm0jgDd89i6PhSE,15534
106
+ vellum_ee/workflows/display/utils/expressions.py,sha256=H5_yWpX4QQNr83iIIocOHddTpktsHbVDp1ylayvjjTE,15840
107
107
  vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3Vd15ijtSN0V97bpW8,1513
108
108
  vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
109
  vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
@@ -144,7 +144,7 @@ vellum/client/README.md,sha256=CuGUYnaE0Imt0KqQ4sIPaUghCjLHkF3DdEvZWu14-8s,4807
144
144
  vellum/client/__init__.py,sha256=AYopGv2ZRVn3zsU8_km6KOvEHDbXiTPCVuYVI7bWvdA,120166
145
145
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
146
146
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
147
- vellum/client/core/client_wrapper.py,sha256=k8n4pGAeg-w1LRqjPGuiXrSk0gbuNUUd8vHKs8aA6Lg,1869
147
+ vellum/client/core/client_wrapper.py,sha256=WsBBC0YLC-9nYHit_KXXwpCcD6m2VY4xxUFnXvAE9Zw,1869
148
148
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
149
149
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
150
150
  vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
@@ -834,13 +834,16 @@ vellum/evaluations/utils/env.py,sha256=Xj_nxsoU5ox06EOTjRopR4lrigQI6Le6qbWGltYoE
834
834
  vellum/evaluations/utils/exceptions.py,sha256=dXMAkzqbHV_AP5FjjbegPlfUE0zQDlpA3qOsoOJUxfg,49
835
835
  vellum/evaluations/utils/paginator.py,sha256=rEED_BJAXAM6tM1yMwHePNzszjq_tTq4NbQvi1jWQ_Q,697
836
836
  vellum/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
837
- vellum/plugins/pydantic.py,sha256=j4ApZ5WyaoCX6v54qNoShhMFyft_uKWTkyAFz5wbcgg,3619
837
+ vellum/plugins/pydantic.py,sha256=GmNsxupKskbqpe4N5NBmSnLo680EATqhXJHABgf1NO0,3727
838
+ vellum/plugins/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
839
+ vellum/plugins/tests/test_pydantic.py,sha256=S6bLqs3Y5DGA012QV_7f6hk4e6Bz-iJ9py9DEKuo4fM,746
838
840
  vellum/plugins/utils.py,sha256=cPmxE9R2CK1bki2jKE8rB-G9zMf2pzHjSPDHFPXwd3Q,878
839
841
  vellum/plugins/vellum_mypy.py,sha256=hfjC2rIxNdQuJa6fIN4PDgODnQ3YaUszyaV2eNbLJlE,27952
840
- vellum/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
841
- vellum/prompts/blocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
842
+ vellum/prompts/__init__.py,sha256=kn-btvQOMNnnBuyQiUpie48_VBJAk7tFFU-ul5dwK60,107
843
+ vellum/prompts/blocks/__init__.py,sha256=Zwvncjd8sUCPmT-8pFpgLYsKJl0xB6td1GTQzjV9hYA,108
842
844
  vellum/prompts/blocks/compilation.py,sha256=qeC_4La5auQkm4EyzCMpN34F5R8mjiGcLV7IxKgVf3k,9973
843
845
  vellum/prompts/blocks/exceptions.py,sha256=vmk5PV6Vyw9nKjZYQDUDW0LH8MfQNIgFvFb_mFWdIRI,50
846
+ vellum/prompts/blocks/helpers.py,sha256=7G1Qi6N3V1K73iqTypMTcl6Rum7wk449yHStkakLy-U,961
844
847
  vellum/prompts/blocks/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
845
848
  vellum/prompts/blocks/tests/test_compilation.py,sha256=EOUtdzJDFGbGhoc_y5XTMyO0HOpOM7FYJssPzd_yRVg,5235
846
849
  vellum/prompts/blocks/types.py,sha256=6aSJQco-5kKeadfKVVXF_SrQPlIJgMYVNc-C7so1sY8,975
@@ -1528,7 +1531,7 @@ vellum/workflows/events/types.py,sha256=LwgFlMRbptJvdPtPO1POUtGtbhGw7BSuvgHxNSgS
1528
1531
  vellum/workflows/events/workflow.py,sha256=7rb1evKsTCRUdCnisAM5Anc19ZKGxnsmzwOOv6jwWEI,7761
1529
1532
  vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
1530
1533
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1531
- vellum/workflows/expressions/accessor.py,sha256=1kY0sYfednQ_u0kh1Df2WTsDREVIOzU7UuZc5jvj5xw,2861
1534
+ vellum/workflows/expressions/accessor.py,sha256=4OvF9JQKHSyYLTz7gGprcw3Da5-zA8D0Co0j1eIeer0,4141
1532
1535
  vellum/workflows/expressions/and_.py,sha256=I7lNqrUM3-m_5hmjjiMhaHhJtKcLj39kEFVWPDOqwfo,916
1533
1536
  vellum/workflows/expressions/begins_with.py,sha256=FnWsQXbENm0ZwkfEP7dR8Qx4_MMrzj6C1yqAV2KaNHw,1123
1534
1537
  vellum/workflows/expressions/between.py,sha256=dVeddT6YA91eOAlE1Utg7C7gnCiYE7WP-dg17yXUeAY,1492
@@ -1559,7 +1562,7 @@ vellum/workflows/expressions/not_in.py,sha256=pFvwkFPsn3WJw61ssFgM2U1dqWEeglfz4F
1559
1562
  vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
1560
1563
  vellum/workflows/expressions/parse_json.py,sha256=xsk6j3HF7bU1yF6fwt5P9Ugcyd5D9ZXrdng11FRilUI,1088
1561
1564
  vellum/workflows/expressions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1562
- vellum/workflows/expressions/tests/test_accessor.py,sha256=ijmGjS1pipdurAgYGmfV8Gcle4HIJ5lQHVG18xHjNiQ,5839
1565
+ vellum/workflows/expressions/tests/test_accessor.py,sha256=g2z0mJjuWwVKeXS0yGoFW-aRmT5n_LrhfuBorSmj9kI,7585
1563
1566
  vellum/workflows/expressions/tests/test_expressions.py,sha256=3b6k8xs-CItBBw95NygFLUNoNPKxI4VA1GyWbkMtqyI,11623
1564
1567
  vellum/workflows/expressions/tests/test_parse_json.py,sha256=zpB_qE5_EwWQL7ULQUJm0o1PRSfWZdAqZNW6Ah13oJE,1059
1565
1568
  vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
@@ -1674,7 +1677,7 @@ vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=jwL1sbitmm1
1674
1677
  vellum/workflows/nodes/experimental/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1675
1678
  vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py,sha256=XK1H_QAT_nVFmFP442RYyPvpTfSgtU6kSGu3-OQPBNU,5072
1676
1679
  vellum/workflows/nodes/experimental/tool_calling_node/tests/test_utils.py,sha256=-g90SdXscuikF7JP0lFGvSvPc8jl2vBuHwBeiYJIiXk,1719
1677
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=U5JjMXg_NV_5XavydhOhsS4_EUoQ42KlEVBe_JiSR4g,12683
1680
+ vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=t18ye2wY9jtTjKXJs8oChNGI5xCdxlUUyVYbRJfJLFI,12959
1678
1681
  vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
1679
1682
  vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1680
1683
  vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
@@ -1707,7 +1710,7 @@ vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2
1707
1710
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1708
1711
  vellum/workflows/state/base.py,sha256=WIMJYyuHUrP4zt0Nudk66HAK1L6GgGmsU_GQp7BGE2U,22189
1709
1712
  vellum/workflows/state/context.py,sha256=KOAI1wEGn8dGmhmAemJaf4SZbitP3jpIBcwKfznQaRE,3076
1710
- vellum/workflows/state/encoder.py,sha256=I21qwYW3edIqlp78jdbjQgtou0hXYKvmofF-5_BCIlU,2552
1713
+ vellum/workflows/state/encoder.py,sha256=xtKpDAR5qWbxIN61dz3X_5_D47MTrbvvas8n15jys18,2881
1711
1714
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
1712
1715
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1713
1716
  vellum/workflows/state/tests/test_state.py,sha256=YOiC9qZAzkdiqb7nRarNWeDwxo7xHv3y3czlHl81ezg,6741
@@ -1734,7 +1737,7 @@ vellum/workflows/utils/tests/test_names.py,sha256=aOqpyvMsOEK_9mg_-yaNxQDW7QQfwq
1734
1737
  vellum/workflows/utils/tests/test_uuids.py,sha256=i77ABQ0M3S-aFLzDXHJq_yr5FPkJEWCMBn1HJ3DObrE,437
1735
1738
  vellum/workflows/utils/tests/test_vellum_variables.py,sha256=vbnKgm41aB5OXlO-ZIPbhQ6xDiZkT8KMxCLqz4zocWY,1791
1736
1739
  vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrgTY,739
1737
- vellum/workflows/utils/vellum_variables.py,sha256=tDWGyE8CfxYaCQQfMwTHqQNgK5V5sJMuhjLsliT8Ar4,4286
1740
+ vellum/workflows/utils/vellum_variables.py,sha256=zVzZSRWKV64fmu9MaoEVebW9r8UsXPvRPMvOuBmwI24,5307
1738
1741
  vellum/workflows/vellum_client.py,sha256=xkfoucodxNK5JR2-lbRqZx3xzDgExWkP6kySrpi_Ubc,1079
1739
1742
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1740
1743
  vellum/workflows/workflows/base.py,sha256=ehJSHDf1vuNjJtKryqKgb5HKGtx_D-wWUiJNBWZ50JU,24347
@@ -1742,8 +1745,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1742
1745
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1743
1746
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=fROqff6AZpCIzaSwOKSdtYy4XR0UZQ6ejxL3RJOSJVs,20447
1744
1747
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1745
- vellum_ai-0.14.76.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1746
- vellum_ai-0.14.76.dist-info/METADATA,sha256=nFVcjTVyBzDC1adJnQXZ6pqbwmtQ9gLYgHTlB5mSyik,5556
1747
- vellum_ai-0.14.76.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1748
- vellum_ai-0.14.76.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1749
- vellum_ai-0.14.76.dist-info/RECORD,,
1748
+ vellum_ai-0.14.78.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1749
+ vellum_ai-0.14.78.dist-info/METADATA,sha256=xCf9xhdMAXgdPDg2CNV4V4OD2yv1oviC8QRS5hi8IIk,5556
1750
+ vellum_ai-0.14.78.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1751
+ vellum_ai-0.14.78.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1752
+ vellum_ai-0.14.78.dist-info/RECORD,,
@@ -1,6 +1,9 @@
1
+ from typing import List
2
+
1
3
  from deepdiff import DeepDiff
2
4
 
3
5
  from vellum import ChatMessagePromptBlock, FunctionDefinition, JinjaPromptBlock
6
+ from vellum.client.types.chat_message import ChatMessage
4
7
  from vellum.workflows import BaseWorkflow
5
8
  from vellum.workflows.inputs import BaseInputs
6
9
  from vellum.workflows.nodes import InlinePromptNode
@@ -554,3 +557,52 @@ def test_serialize_workflow_with_nested_descriptor_blocks():
554
557
  "type": "DICTIONARY_REFERENCE",
555
558
  }
556
559
  ]
560
+
561
+
562
+ def test_inline_prompt_node__coalesce_expression_serialization():
563
+ """
564
+ Tests that prompt nodes can serialize coalesce expressions like State.chat_history.coalesce([]).
565
+ """
566
+
567
+ # GIVEN a custom state with chat_history
568
+ class MyState(BaseState):
569
+ chat_history: List[ChatMessage] = []
570
+
571
+ # AND a prompt node that uses a coalesce expression as input
572
+ class MyNode(InlinePromptNode[MyState]):
573
+ ml_model = "gpt-4o"
574
+ blocks = []
575
+ prompt_inputs = {
576
+ "chat_history": MyState.chat_history.coalesce([]),
577
+ }
578
+
579
+ class TestWorkflow(BaseWorkflow[BaseInputs, MyState]):
580
+ graph = MyNode
581
+
582
+ # WHEN the node is serialized
583
+ workflow_display = get_workflow_display(workflow_class=TestWorkflow)
584
+ serialized: dict = workflow_display.serialize()
585
+
586
+ # THEN the prompt is serialized with the correct inputs
587
+ prompt_nodes = [node for node in serialized["workflow_raw_data"]["nodes"] if node["type"] == "PROMPT"]
588
+ prompt_node = prompt_nodes[0]
589
+
590
+ prompt_inputs_attr = next((attr for attr in prompt_node["attributes"] if attr["name"] == "prompt_inputs"), None)
591
+ assert prompt_inputs_attr
592
+ assert prompt_inputs_attr["value"]["type"] == "DICTIONARY_REFERENCE"
593
+ chat_history_entry = prompt_inputs_attr["value"]["entries"][0]
594
+
595
+ assert chat_history_entry["key"] == "chat_history"
596
+ assert chat_history_entry["value"]["type"] == "BINARY_EXPRESSION"
597
+ assert chat_history_entry["value"]["operator"] == "coalesce"
598
+ assert chat_history_entry["value"]["lhs"] == {
599
+ "type": "WORKFLOW_STATE",
600
+ "state_variable_id": "6012a4f7-a8ff-464d-bd62-7c41fde06fa4",
601
+ }
602
+ assert chat_history_entry["value"]["rhs"] == {
603
+ "type": "CONSTANT_VALUE",
604
+ "value": {
605
+ "type": "JSON",
606
+ "value": [],
607
+ },
608
+ }
@@ -146,7 +146,7 @@ def test_serialize_workflow():
146
146
  "forced": None,
147
147
  "strict": None,
148
148
  },
149
- "src": 'def get_current_weather(location: str, unit: str) -> str:\n """\n Get the current weather in a given location.\n """\n return f"The current weather in {location} is sunny with a temperature of 70 degrees {unit}."\n', # noqa: E501
149
+ "src": 'import math\n\n\ndef get_current_weather(location: str, unit: str) -> str:\n """\n Get the current weather in a given location.\n """\n return f"The current weather in {location} is sunny with a temperature of {get_temperature(70.1)} degrees {unit}."\n\n\ndef get_temperature(temperature: float) -> int:\n """\n Get the temperature in a given location.\n """\n return math.floor(temperature)\n', # noqa: E501
150
150
  }
151
151
  ],
152
152
  },
@@ -1,5 +1,16 @@
1
+ from datetime import datetime
2
+
1
3
  from deepdiff import DeepDiff
2
4
 
5
+ from vellum.client.types.release_environment import ReleaseEnvironment
6
+ from vellum.client.types.release_release_tag import ReleaseReleaseTag
7
+ from vellum.client.types.release_review_reviewer import ReleaseReviewReviewer
8
+ from vellum.client.types.slim_release_review import SlimReleaseReview
9
+ from vellum.client.types.workflow_deployment_release import WorkflowDeploymentRelease
10
+ from vellum.client.types.workflow_deployment_release_workflow_deployment import (
11
+ WorkflowDeploymentReleaseWorkflowDeployment,
12
+ )
13
+ from vellum.client.types.workflow_deployment_release_workflow_version import WorkflowDeploymentReleaseWorkflowVersion
3
14
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
4
15
 
5
16
  from tests.workflows.basic_tool_calling_node_workflow_deployment.workflow import (
@@ -7,7 +18,41 @@ from tests.workflows.basic_tool_calling_node_workflow_deployment.workflow import
7
18
  )
8
19
 
9
20
 
10
- def test_serialize_workflow():
21
+ def test_serialize_workflow(vellum_client):
22
+ vellum_client.release_reviews.retrieve_workflow_deployment_release.return_value = WorkflowDeploymentRelease(
23
+ id="test-id",
24
+ created=datetime.now(),
25
+ environment=ReleaseEnvironment(
26
+ id="test-id",
27
+ name="test-name",
28
+ label="test-label",
29
+ ),
30
+ created_by=None,
31
+ workflow_version=WorkflowDeploymentReleaseWorkflowVersion(
32
+ id="test-id",
33
+ input_variables=[],
34
+ output_variables=[],
35
+ ),
36
+ deployment=WorkflowDeploymentReleaseWorkflowDeployment(name="test-name"),
37
+ description="test-description", # main mock
38
+ release_tags=[
39
+ ReleaseReleaseTag(
40
+ name="test-name",
41
+ source="USER",
42
+ )
43
+ ],
44
+ reviews=[
45
+ SlimReleaseReview(
46
+ id="test-id",
47
+ created=datetime.now(),
48
+ reviewer=ReleaseReviewReviewer(
49
+ id="test-id",
50
+ full_name="test-name",
51
+ ),
52
+ state="APPROVED",
53
+ )
54
+ ],
55
+ )
11
56
  # GIVEN a Workflow that uses a generic node
12
57
  # WHEN we serialize it
13
58
  workflow_display = get_workflow_display(workflow_class=BasicToolCallingNodeWorkflowDeploymentWorkflow)
@@ -51,8 +96,8 @@ def test_serialize_workflow():
51
96
  "value": [
52
97
  {
53
98
  "type": "WORKFLOW_DEPLOYMENT",
54
- "name": "deployment_1",
55
- "description": "Workflow deployment for deployment_1",
99
+ "name": "test-name",
100
+ "description": "test-description",
56
101
  "deployment": "deployment_1",
57
102
  "release_tag": "LATEST",
58
103
  }
@@ -346,14 +346,20 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
346
346
  }
347
347
 
348
348
  if isinstance(value, DeploymentDefinition):
349
+ workflow_deployment_release = display_context.client.release_reviews.retrieve_workflow_deployment_release(
350
+ value.deployment, value.release_tag
351
+ )
352
+ name = workflow_deployment_release.deployment.name or value.deployment
353
+ description = workflow_deployment_release.description or f"Workflow Deployment for {name}"
354
+
349
355
  return {
350
356
  "type": "CONSTANT_VALUE",
351
357
  "value": {
352
358
  "type": "JSON",
353
359
  "value": {
354
360
  "type": "WORKFLOW_DEPLOYMENT",
355
- "name": value.deployment,
356
- "description": f"Workflow deployment for {value.deployment}",
361
+ "name": name,
362
+ "description": description,
357
363
  "deployment": value.deployment,
358
364
  "release_tag": value.release_tag,
359
365
  },