vellum-ai 0.14.9__py3-none-any.whl → 0.14.10__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.9",
21
+ "X-Fern-SDK-Version": "0.14.10",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -2,6 +2,7 @@ import json
2
2
  from typing import Any, Dict, Optional
3
3
 
4
4
  from jinja2.sandbox import SandboxedEnvironment
5
+ from pydantic import BaseModel
5
6
 
6
7
  from vellum.utils.templating.constants import FilterFunc
7
8
  from vellum.utils.templating.exceptions import JinjaTemplateError
@@ -12,6 +13,9 @@ def finalize(obj: Any) -> str:
12
13
  if isinstance(obj, (dict, list)):
13
14
  return json.dumps(obj, cls=DefaultStateEncoder)
14
15
 
16
+ if isinstance(obj, BaseModel):
17
+ return json.dumps(obj.model_dump(), cls=DefaultStateEncoder)
18
+
15
19
  return str(obj)
16
20
 
17
21
 
@@ -23,7 +27,6 @@ def render_sandboxed_jinja_template(
23
27
  jinja_globals: Optional[Dict[str, Any]] = None,
24
28
  ) -> str:
25
29
  """Render a Jinja template within a sandboxed environment."""
26
-
27
30
  try:
28
31
  environment = SandboxedEnvironment(
29
32
  keep_trailing_newline=True,
@@ -39,14 +39,15 @@ class _BaseWorkflowEvent(BaseEvent):
39
39
  return self.body.workflow_definition
40
40
 
41
41
 
42
- class NodeDisplay(UniversalBaseModel):
42
+ class NodeEventDisplayContext(UniversalBaseModel):
43
43
  input_display: Dict[str, UUID]
44
44
  output_display: Dict[str, UUID]
45
45
  port_display: Dict[str, UUID]
46
+ subworkflow_display: Optional["WorkflowEventDisplayContext"] = None
46
47
 
47
48
 
48
49
  class WorkflowEventDisplayContext(UniversalBaseModel):
49
- node_displays: Dict[str, NodeDisplay]
50
+ node_displays: Dict[str, NodeEventDisplayContext]
50
51
  workflow_inputs: Dict[str, UUID]
51
52
  workflow_outputs: Dict[str, UUID]
52
53
 
@@ -82,7 +82,6 @@ class TemplatingNode(BaseNode[StateType], Generic[StateType, _OutputType], metac
82
82
  def run(self) -> Outputs:
83
83
  rendered_template = self._render_template()
84
84
  result = self._cast_rendered_template(rendered_template)
85
-
86
85
  return self.Outputs(result=result)
87
86
 
88
87
  def _render_template(self) -> str:
@@ -234,3 +234,52 @@ def test_templating_node__last_chat_message():
234
234
 
235
235
  # THEN the output is the expected JSON
236
236
  assert outputs.result == [ChatMessage(role="USER", text="Hello")]
237
+
238
+
239
+ def test_templating_node__function_call_value_input():
240
+ # GIVEN a templating node that receives a FunctionCallVellumValue
241
+ class FunctionCallTemplateNode(TemplatingNode[BaseState, FunctionCall]):
242
+ template = """{{ function_call }}"""
243
+ inputs = {
244
+ "function_call": FunctionCallVellumValue(
245
+ type="FUNCTION_CALL",
246
+ value=FunctionCall(name="test_function", arguments={"key": "value"}, id="test_id", state="FULFILLED"),
247
+ )
248
+ }
249
+
250
+ # WHEN the node is run
251
+ node = FunctionCallTemplateNode()
252
+ outputs = node.run()
253
+
254
+ # THEN the output is the expected function call
255
+ assert outputs.result == FunctionCall(
256
+ name="test_function", arguments={"key": "value"}, id="test_id", state="FULFILLED"
257
+ )
258
+
259
+
260
+ def test_templating_node__function_call_as_json():
261
+ # GIVEN a node that receives a FunctionCallVellumValue but outputs as Json
262
+ class JsonOutputNode(TemplatingNode[BaseState, Json]):
263
+ template = """{{ function_call }}"""
264
+ inputs = {
265
+ "function_call": FunctionCallVellumValue(
266
+ type="FUNCTION_CALL",
267
+ value=FunctionCall(name="test_function", arguments={"key": "value"}, id="test_id", state="FULFILLED"),
268
+ )
269
+ }
270
+
271
+ # WHEN the node is run
272
+ node = JsonOutputNode()
273
+ outputs = node.run()
274
+
275
+ # THEN we get just the FunctionCall data as JSON
276
+ assert outputs.result == {
277
+ "name": "test_function",
278
+ "arguments": {"key": "value"},
279
+ "id": "test_id",
280
+ "state": "FULFILLED",
281
+ }
282
+
283
+ # AND we can access fields directly
284
+ assert outputs.result["arguments"] == {"key": "value"}
285
+ assert outputs.result["name"] == "test_function"
@@ -109,7 +109,11 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
109
109
 
110
110
  if output_type is Json:
111
111
  try:
112
- return json.loads(result_as_str)
112
+ data = json.loads(result_as_str)
113
+ # If we got a FunctionCallVellumValue, return just the value
114
+ if isinstance(data, dict) and data.get("type") == "FUNCTION_CALL" and "value" in data:
115
+ return data["value"]
116
+ return data
113
117
  except json.JSONDecodeError:
114
118
  raise ValueError("Invalid JSON format for result_as_str")
115
119
 
@@ -124,9 +128,16 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
124
128
  if issubclass(output_type, BaseModel):
125
129
  try:
126
130
  data = json.loads(result_as_str)
131
+ # If we got a FunctionCallVellumValue extract FunctionCall,
132
+ if (
133
+ hasattr(output_type, "__name__")
134
+ and output_type.__name__ == "FunctionCall"
135
+ and isinstance(data, dict)
136
+ and "value" in data
137
+ ):
138
+ data = data["value"]
139
+ return output_type.model_validate(data)
127
140
  except json.JSONDecodeError:
128
141
  raise ValueError("Invalid JSON format for result_as_str")
129
142
 
130
- return output_type.model_validate(data)
131
-
132
143
  raise ValueError(f"Unsupported output type: {output_type}")
@@ -328,11 +328,7 @@ class WorkflowRunner(Generic[StateType]):
328
328
  node_definition=node.__class__,
329
329
  error=e.error,
330
330
  ),
331
- parent=WorkflowParentContext(
332
- span_id=span_id,
333
- workflow_definition=self.workflow.__class__,
334
- parent=self._parent_context,
335
- ),
331
+ parent=parent_context,
336
332
  )
337
333
  )
338
334
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.9
3
+ Version: 0.14.10
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -89,9 +89,9 @@ vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh
89
89
  vellum_ee/workflows/display/utils/vellum.py,sha256=UjK_RxnSEmlIu9klGCPWU5RAQBmgZ7cRbRdgxaTbubE,8081
90
90
  vellum_ee/workflows/display/vellum.py,sha256=7mqQaKZPPrLMcXSAQkPIxCy5x8HkKs5PbCu3GRaC2o8,8507
91
91
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
92
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=p9Lt1AypyC0Y6WHOUCsk4lYmlgODwg5oz-IDo0UA-1o,18678
92
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=Z9s_rtqAH5IfBtXOw40gbiT0GZeN6JRdFwGgPT-DIYM,20143
93
93
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
94
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=_yB3-u7_bWdD4lUBWpRdWztJmJL-DXkkZaw9Vy9HH6g,3245
94
+ vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=STVSG0eL97mdnwBA5nOOgW8AuK8k-b8kWDyHKatNXIA,4259
95
95
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=mbAzCpswOek34ITeTkesbVreCXpulj4NFjIg3RcdVZ8,18243
96
96
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
97
  vellum_ee/workflows/server/virtual_file_loader.py,sha256=X_DdNK7MfyOjKWekk6YQpOSCT6klKcdjT6nVJcBH1sM,1481
@@ -114,7 +114,7 @@ vellum_ee/workflows/tests/local_workflow/nodes/__init__.py,sha256=1F6jxUpSKfPXPj
114
114
  vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zirg0w9VKUW3QVDSdBLDqcqAMZjCL_oWbpU,297
115
115
  vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
116
116
  vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
117
- vellum_ee/workflows/tests/test_display_meta.py,sha256=pzdqND4KLWs7EUIbpXuqgso7BIRpoUsO3T_bgeENs0Q,2205
117
+ vellum_ee/workflows/tests/test_display_meta.py,sha256=xLQ7QtIXIiIm61pm2lyl588Ohzc3pjyq1nDp-qVu2Fs,2295
118
118
  vellum_ee/workflows/tests/test_server.py,sha256=M6vvQ2hjIpDWtQdDM9EPbMvUrZ93niAuYnxMNJWOjPA,511
119
119
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
120
120
  vellum/__init__.py,sha256=a_aM1_A04XGma4MAIDNeBF9BKzWbiQaVVMRzImHuxjA,36438
@@ -122,7 +122,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
122
122
  vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
123
123
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
124
124
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
125
- vellum/client/core/client_wrapper.py,sha256=RhAfDORGTXyVqWFHTHUIONrGJRy7OCD049LPC0pwN_k,1868
125
+ vellum/client/core/client_wrapper.py,sha256=6EXwOd5Ka1mFWVlZmNuffO99dtW8DTAiRFes4vDlQFY,1869
126
126
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
127
127
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
128
128
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -1287,7 +1287,7 @@ vellum/utils/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
1287
1287
  vellum/utils/templating/constants.py,sha256=8OHMO6WFAEimbIaiHc5gy6s91D7_KvW-vTlEMWwvl_M,711
1288
1288
  vellum/utils/templating/custom_filters.py,sha256=XVHriwazejRZmxB_eg4xHgCxl7AiQQ2sx-hRLMmylfU,885
1289
1289
  vellum/utils/templating/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
1290
- vellum/utils/templating/render.py,sha256=5OsD1e9fks1aysYTyPKjYGaloYUbIKWpajcxtjtiFuU,2037
1290
+ vellum/utils/templating/render.py,sha256=P2t9qU4w_WdXAVLM5Nj3bc1-XlIKOkwK-czQ80pHBag,2172
1291
1291
  vellum/utils/templating/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1292
1292
  vellum/utils/templating/tests/test_custom_filters.py,sha256=mkJwc7t1gE13SKgPxhF-lN_m2XGCkphCB9Te81dGekI,532
1293
1293
  vellum/utils/typing.py,sha256=wx_daFqD69cYkuJTVnvNrpjhqC3uuhbnyJ9_bIwC9OU,327
@@ -1315,7 +1315,7 @@ vellum/workflows/events/node.py,sha256=uHT6If0esgZ3nLjrjmUPTKf3qbjGhoV_x5YKpjDBD
1315
1315
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1316
1316
  vellum/workflows/events/tests/test_event.py,sha256=uRfMwSOqU-ROeZKCEngGvvJYlOZuxBhnC3qH5AGi3fM,15399
1317
1317
  vellum/workflows/events/types.py,sha256=cjRE8WL8tYCFradd9NOGl_H0mN3LiWWnA1uHmyT2Q0Q,3412
1318
- vellum/workflows/events/workflow.py,sha256=XtmGG7NRp0TQ4memOJPcaNOs7qMUBbd4WKSZVlxWrCk,5937
1318
+ vellum/workflows/events/workflow.py,sha256=sLO29djAeHGVd4hLhaNtOQ48uwUjfl-DotZQt06PxQA,6033
1319
1319
  vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
1320
1320
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1321
1321
  vellum/workflows/expressions/accessor.py,sha256=ItZF7fMLzVTqsdAiaXb5SiDupXmX0X9xbIus1W6hRds,1870
@@ -1381,8 +1381,8 @@ vellum/workflows/nodes/core/retry_node/node.py,sha256=Vt3fx4G-DRIb9a-IHIUfaAclgf
1381
1381
  vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1382
1382
  vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=RM_OHwxrHwyxvlQQBJPqVBxpedFuWQ9h2-Xa3kP75sc,4399
1383
1383
  vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
1384
- vellum/workflows/nodes/core/templating_node/node.py,sha256=Vqlg4L-5XNuIdbZKQe-GEYqTIV7iXNjLO7QIRgz4ujc,3722
1385
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=nY2P6r7cW85k7NEKXUFNeDTMWlz8ZEZyMY2Sg-0qO_E,7327
1384
+ vellum/workflows/nodes/core/templating_node/node.py,sha256=-JIqLUv6Xpx_LTVZt7whQ2X2VatgHDdTxjMrz64luEs,3721
1385
+ vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=ldnmSASx0TfAnT3ZvU0AXtN0diZGrfySiXipuJIIzWU,9055
1386
1386
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1387
1387
  vellum/workflows/nodes/core/try_node/node.py,sha256=_0df2_6kim8pW4hB7IXUJOYS4fLduaeGDH4rRhYYvcg,4212
1388
1388
  vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1448,7 +1448,7 @@ vellum/workflows/nodes/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
1448
1448
  vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
1449
1449
  vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=1EGeiaT-Zoo6pttQFKKBcdf3dmhAbjKGaErYD5FFwlc,10185
1450
1450
  vellum/workflows/nodes/mocks.py,sha256=gvM2tyoe-V84jFbFdhQsyGAPyQBzmjn_CkhT_yxccgY,499
1451
- vellum/workflows/nodes/utils.py,sha256=YRj3qIz6--N6CuDiKalsWpBmUR6z7WcRhqtLxoenDZE,4354
1451
+ vellum/workflows/nodes/utils.py,sha256=fq49n624n_nBMIFcc0JiNkHy3qna-bsA1HZRPa5AOJE,4918
1452
1452
  vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
1453
1453
  vellum/workflows/outputs/base.py,sha256=2MtTlyzePopMZDpBWQIV8HRV-km1-z0vI8Gm012q9OQ,8665
1454
1454
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
@@ -1470,7 +1470,7 @@ vellum/workflows/references/workflow_input.py,sha256=86IuhlBz-9cGxeUzizyjdp482aj
1470
1470
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1471
1471
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1472
1472
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1473
- vellum/workflows/runner/runner.py,sha256=VUGw-QlUNyfwRWjXgBZ1VqKRuYdFC7YdtAmQcsgR6I0,31206
1473
+ vellum/workflows/runner/runner.py,sha256=_p19T1woplSxZGabZuSUFBKSYBrXADrM7b_1YnWVN3g,31013
1474
1474
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1475
1475
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1476
1476
  vellum/workflows/state/base.py,sha256=Vkhneko3VlQrPsMLU1PYSzXU_W1u7_AraJsghiv5O-4,15512
@@ -1506,8 +1506,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1506
1506
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1507
1507
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=NRteiICyJvDM5zrtUfq2fZoXcGQVaWC9xmNlLLVW0cU,7979
1508
1508
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1509
- vellum_ai-0.14.9.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1510
- vellum_ai-0.14.9.dist-info/METADATA,sha256=JO_Tqa4ayXrL4ytBbrfiXfly8yXeiPyQc-4NOoL9kDk,5407
1511
- vellum_ai-0.14.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1512
- vellum_ai-0.14.9.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1513
- vellum_ai-0.14.9.dist-info/RECORD,,
1509
+ vellum_ai-0.14.10.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1510
+ vellum_ai-0.14.10.dist-info/METADATA,sha256=XNLaqqk85P8uRcr65-_IDfNPVi1dpNP9YvZgm43wAGs,5408
1511
+ vellum_ai-0.14.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1512
+ vellum_ai-0.14.10.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1513
+ vellum_ai-0.14.10.dist-info/RECORD,,
@@ -9,7 +9,7 @@ from typing import Any, Dict, Generic, Iterator, List, Optional, Tuple, Type, Un
9
9
  from vellum.workflows import BaseWorkflow
10
10
  from vellum.workflows.descriptors.base import BaseDescriptor
11
11
  from vellum.workflows.edges import Edge
12
- from vellum.workflows.events.workflow import NodeDisplay, WorkflowEventDisplayContext
12
+ from vellum.workflows.events.workflow import NodeEventDisplayContext, WorkflowEventDisplayContext
13
13
  from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
14
14
  from vellum.workflows.nodes.bases import BaseNode
15
15
  from vellum.workflows.nodes.utils import get_wrapped_node
@@ -34,7 +34,9 @@ from vellum_ee.workflows.display.base import (
34
34
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
35
35
  from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
36
36
  from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
37
+ from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
37
38
  from vellum_ee.workflows.display.types import NodeDisplayType, WorkflowDisplayContext
39
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
38
40
 
39
41
  logger = logging.getLogger(__name__)
40
42
 
@@ -383,10 +385,15 @@ class BaseWorkflowDisplay(
383
385
  except ModuleNotFoundError:
384
386
  return None
385
387
 
386
- display_context = display_class.WorkflowDisplay(workflow_class).display_context
387
- if not isinstance(display_context, WorkflowDisplayContext):
388
+ workflow_display = display_class.WorkflowDisplay(workflow_class)
389
+ if not isinstance(workflow_display, BaseWorkflowDisplay):
388
390
  return None
389
391
 
392
+ return workflow_display.get_event_display_context()
393
+
394
+ def get_event_display_context(self):
395
+ display_context = self.display_context
396
+
390
397
  workflow_outputs = {
391
398
  output.name: display_context.workflow_output_displays[output].id
392
399
  for output in display_context.workflow_output_displays
@@ -398,27 +405,44 @@ class BaseWorkflowDisplay(
398
405
  node_displays = {
399
406
  str(node.__id__): display_context.node_displays[node] for node in display_context.node_displays
400
407
  }
401
- temp_node_displays = {}
402
- for node in node_displays:
403
- current_node = node_displays[node]
408
+ node_event_displays = {}
409
+ for node_id in node_displays:
410
+ current_node_display = node_displays[node_id]
404
411
  input_display = {}
405
- if isinstance(current_node, BaseNodeVellumDisplay):
406
- input_display = current_node.node_input_ids_by_name
412
+ if isinstance(current_node_display, BaseNodeVellumDisplay):
413
+ input_display = current_node_display.node_input_ids_by_name
407
414
  node_display_meta = {
408
- output.name: current_node.output_display[output].id for output in current_node.output_display
415
+ output.name: current_node_display.output_display[output].id
416
+ for output in current_node_display.output_display
409
417
  }
410
- port_display_meta = {port.name: current_node.port_displays[port].id for port in current_node.port_displays}
411
-
412
- temp_node_displays[node] = NodeDisplay(
418
+ port_display_meta = {
419
+ port.name: current_node_display.port_displays[port].id for port in current_node_display.port_displays
420
+ }
421
+ node = current_node_display._node
422
+ subworkflow_display_context: Optional[WorkflowEventDisplayContext] = None
423
+ if hasattr(node, "subworkflow"):
424
+ # All nodes that have a subworkflow attribute are currently expected to call them `subworkflow`
425
+ # This will change if in the future we decide to support multiple subworkflows on a single node
426
+ subworkflow_attribute = raise_if_descriptor(getattr(node, "subworkflow"))
427
+ if issubclass(subworkflow_attribute, BaseWorkflow):
428
+ subworkflow_display = get_workflow_display(
429
+ base_display_class=display_context.workflow_display_class,
430
+ workflow_class=subworkflow_attribute,
431
+ parent_display_context=display_context,
432
+ )
433
+ subworkflow_display_context = subworkflow_display.get_event_display_context()
434
+
435
+ node_event_displays[node_id] = NodeEventDisplayContext(
413
436
  input_display=input_display,
414
437
  output_display=node_display_meta,
415
438
  port_display=port_display_meta,
439
+ subworkflow_display=subworkflow_display_context,
416
440
  )
417
441
 
418
442
  display_meta = WorkflowEventDisplayContext(
419
443
  workflow_outputs=workflow_outputs,
420
444
  workflow_inputs=workflow_inputs,
421
- node_displays=temp_node_displays,
445
+ node_displays=node_event_displays,
422
446
  )
423
447
  return display_meta
424
448
 
@@ -1,6 +1,7 @@
1
1
  import pytest
2
2
 
3
3
  from vellum.workflows.nodes.bases.base import BaseNode
4
+ from vellum.workflows.nodes.core.inline_subworkflow_node.node import InlineSubworkflowNode
4
5
  from vellum.workflows.workflows.base import BaseWorkflow
5
6
  from vellum_ee.workflows.display.nodes import BaseNodeDisplay
6
7
  from vellum_ee.workflows.display.vellum import NodeDisplayData, NodeDisplayPosition
@@ -92,3 +93,29 @@ def test_serialize_workflow__node_display_class_not_registered():
92
93
 
93
94
  # THEN it should should succeed
94
95
  assert data is not None
96
+
97
+
98
+ def test_get_event_display_context__node_display_to_include_subworkflow_display():
99
+ # GIVEN a simple workflow
100
+ class InnerNode(BaseNode):
101
+ pass
102
+
103
+ class Subworkflow(BaseWorkflow):
104
+ graph = InnerNode
105
+
106
+ # AND a workflow that includes the subworkflow
107
+ class SubworkflowNode(InlineSubworkflowNode):
108
+ subworkflow = Subworkflow
109
+
110
+ class MyWorkflow(BaseWorkflow):
111
+ graph = SubworkflowNode
112
+
113
+ # WHEN we gather the event display context
114
+ display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
115
+
116
+ # THEN the subworkflow display should be included
117
+ assert str(SubworkflowNode.__id__) in display_context.node_displays
118
+ node_event_display = display_context.node_displays[str(SubworkflowNode.__id__)]
119
+
120
+ assert node_event_display.subworkflow_display is not None
121
+ assert str(InnerNode.__id__) in node_event_display.subworkflow_display.node_displays
@@ -40,11 +40,13 @@ def test_base_class_dynamic_import(files):
40
40
  },
41
41
  "output_display": {"result": UUID("423bc529-1a1a-4f72-af4d-cbdb5f0a5929")},
42
42
  "port_display": {"default": UUID("afda9a19-0618-42e1-9b63-5d0db2a88f62")},
43
+ "subworkflow_display": None,
43
44
  },
44
45
  "f3ef4b2b-fec9-4026-9cc6-e5eac295307f": {
45
46
  "input_display": {"node_input": UUID("fe6cba85-2423-4b5e-8f85-06311a8be5fb")},
46
47
  "output_display": {"value": UUID("5469b810-6ea6-4362-9e79-e360d44a1405")},
47
48
  "port_display": {},
49
+ "subworkflow_display": None,
48
50
  },
49
51
  },
50
52
  "workflow_inputs": {"input_value": UUID("2268a996-bd17-4832-b3ff-f5662d54b306")},