vellum-ai 1.5.0__py3-none-any.whl → 1.5.2__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.
Files changed (38) hide show
  1. vellum/client/core/client_wrapper.py +2 -2
  2. vellum/workflows/descriptors/utils.py +3 -0
  3. vellum/workflows/emitters/vellum_emitter.py +4 -1
  4. vellum/workflows/integrations/__init__.py +5 -0
  5. vellum/workflows/integrations/tests/__init__.py +0 -0
  6. vellum/workflows/integrations/tests/test_vellum_integration_service.py +206 -0
  7. vellum/workflows/integrations/vellum_integration_service.py +98 -0
  8. vellum/workflows/nodes/bases/base.py +24 -3
  9. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +5 -0
  10. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +2 -5
  11. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +38 -4
  12. vellum/workflows/types/definition.py +11 -0
  13. vellum/workflows/utils/functions.py +25 -17
  14. vellum/workflows/workflows/base.py +6 -2
  15. vellum/workflows/workflows/tests/test_base_workflow.py +45 -0
  16. {vellum_ai-1.5.0.dist-info → vellum_ai-1.5.2.dist-info}/METADATA +1 -1
  17. {vellum_ai-1.5.0.dist-info → vellum_ai-1.5.2.dist-info}/RECORD +38 -35
  18. vellum_ee/assets/node-definitions.json +423 -32
  19. vellum_ee/scripts/generate_node_definitions.py +2 -2
  20. vellum_ee/workflows/display/nodes/base_node_display.py +6 -3
  21. vellum_ee/workflows/display/nodes/vellum/api_node.py +4 -7
  22. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +19 -5
  23. vellum_ee/workflows/display/nodes/vellum/map_node.py +17 -3
  24. vellum_ee/workflows/display/nodes/vellum/retry_node.py +2 -3
  25. vellum_ee/workflows/display/nodes/vellum/search_node.py +3 -6
  26. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  27. vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +2 -3
  28. vellum_ee/workflows/display/nodes/vellum/try_node.py +3 -4
  29. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +5 -11
  30. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +1 -1
  31. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +1 -1
  32. vellum_ee/workflows/display/types.py +3 -3
  33. vellum_ee/workflows/display/utils/expressions.py +10 -3
  34. vellum_ee/workflows/display/utils/vellum.py +9 -2
  35. vellum_ee/workflows/display/workflows/base_workflow_display.py +2 -2
  36. {vellum_ai-1.5.0.dist-info → vellum_ai-1.5.2.dist-info}/LICENSE +0 -0
  37. {vellum_ai-1.5.0.dist-info → vellum_ai-1.5.2.dist-info}/WHEEL +0 -0
  38. {vellum_ai-1.5.0.dist-info → vellum_ai-1.5.2.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,110 @@
1
1
  {
2
2
  "nodes": [
3
+ {
4
+ "id": "55e74c90-9901-42fd-92c3-2dd0c5fd4876",
5
+ "display_data": {
6
+ "position": {
7
+ "x": 0.0,
8
+ "y": 0.0
9
+ },
10
+ "comment": {
11
+ "value": "\n Used to execute an API call. This node exists to be backwards compatible with Vellum's API Node, and for most cases,\n you should extend from `BaseAPINode` directly.\n\n url: str - The URL to send the request to.\n method: APIRequestMethod - The HTTP method to use for the request.\n data: Optional[str] - The data to send in the request body.\n json: Optional[\"JsonObject\"] - The JSON data to send in the request body.\n headers: Optional[Dict[str, Union[str, VellumSecret]]] - The headers to send in the request.\n\n authorization_type: Optional[AuthorizationType] = None - The type of authorization to use for the API call.\n api_key_header_key: Optional[str] = None - The header key to use for the API key authorization.\n bearer_token_value: Optional[Union[str, VellumSecretReference]] = None - The bearer token value to use\n for the bearer token authorization.\n ",
12
+ "expanded": true
13
+ }
14
+ },
15
+ "base": {
16
+ "name": "BaseAPINode",
17
+ "module": [
18
+ "vellum",
19
+ "workflows",
20
+ "nodes",
21
+ "displayable",
22
+ "bases",
23
+ "api_node",
24
+ "node"
25
+ ]
26
+ },
27
+ "definition": {
28
+ "name": "APINode",
29
+ "module": [
30
+ "vellum",
31
+ "workflows",
32
+ "nodes",
33
+ "displayable",
34
+ "api_node",
35
+ "node"
36
+ ]
37
+ },
38
+ "trigger": {
39
+ "id": "c2aa7c83-7eba-4996-acb4-85cd4f5bbf58",
40
+ "merge_behavior": "AWAIT_ANY"
41
+ },
42
+ "ports": [
43
+ {
44
+ "id": "f165a94f-33f7-4c09-8896-9e683a39e11c",
45
+ "name": "default",
46
+ "type": "DEFAULT"
47
+ }
48
+ ],
49
+ "attributes": [
50
+ {
51
+ "id": "aacb36f1-c980-460d-8002-43bb57128e05",
52
+ "name": "timeout",
53
+ "value": {
54
+ "type": "CONSTANT_VALUE",
55
+ "value": {
56
+ "type": "JSON",
57
+ "value": null
58
+ }
59
+ }
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ "id": "43b3514f-3490-47d6-9b78-bbc4736f0159",
65
+ "display_data": {
66
+ "position": {
67
+ "x": 0.0,
68
+ "y": 0.0
69
+ },
70
+ "comment": {
71
+ "value": "\n Used to execute an arbitrary script. This node exists to be backwards compatible with\n Vellum's Code Execution Node, and for most cases, you should extend from `BaseNode` directly.\n\n filepath: str - The path to the script to execute.\n code_inputs: EntityInputsInterface - The inputs for the custom script.\n runtime: CodeExecutionRuntime = \"PYTHON_3_12\" - The runtime to use for the custom script.\n packages: Optional[Sequence[CodeExecutionPackage]] = None - The packages to use for the custom script.\n request_options: Optional[RequestOptions] = None - The request options to use for the custom script.\n ",
72
+ "expanded": true
73
+ }
74
+ },
75
+ "base": {
76
+ "name": "BaseNode",
77
+ "module": [
78
+ "vellum",
79
+ "workflows",
80
+ "nodes",
81
+ "bases",
82
+ "base"
83
+ ]
84
+ },
85
+ "definition": {
86
+ "name": "CodeExecutionNode",
87
+ "module": [
88
+ "vellum",
89
+ "workflows",
90
+ "nodes",
91
+ "displayable",
92
+ "code_execution_node",
93
+ "node"
94
+ ]
95
+ },
96
+ "trigger": {
97
+ "id": "fb1b8b44-5e7a-481b-a163-96e7e08219d5",
98
+ "merge_behavior": "AWAIT_ANY"
99
+ },
100
+ "ports": [
101
+ {
102
+ "id": "e66138d5-e50d-40d8-bc27-00545ce5e283",
103
+ "name": "default",
104
+ "type": "DEFAULT"
105
+ }
106
+ ]
107
+ },
3
108
  {
4
109
  "id": "5d3c60ce-7acf-4d8b-a68d-65cc0b561ac5",
5
110
  "display_data": {
@@ -78,6 +183,320 @@
78
183
  },
79
184
  "ports": []
80
185
  },
186
+ {
187
+ "id": "55d0f8e9-acbd-4d33-85ba-c7527348f62a",
188
+ "display_data": {
189
+ "position": {
190
+ "x": 0.0,
191
+ "y": 0.0
192
+ },
193
+ "comment": {
194
+ "value": "\n Used to execute a Prompt defined inline.\n\n prompt_inputs: EntityInputsInterface - The inputs for the Prompt\n ml_model: str - Either the ML Model's UUID or its name.\n blocks: List[PromptBlock] - The blocks that make up the Prompt\n functions: Optional[List[FunctionDefinition]] - The functions to include in the Prompt\n parameters: PromptParameters - The parameters for the Prompt\n expand_meta: Optional[AdHocExpandMeta] - Expandable execution fields to include in the response\n request_options: Optional[RequestOptions] - The request options to use for the Prompt Execution\n ",
195
+ "expanded": true
196
+ }
197
+ },
198
+ "base": {
199
+ "name": "BaseInlinePromptNode",
200
+ "module": [
201
+ "vellum",
202
+ "workflows",
203
+ "nodes",
204
+ "displayable",
205
+ "bases",
206
+ "inline_prompt_node",
207
+ "node"
208
+ ]
209
+ },
210
+ "definition": {
211
+ "name": "InlinePromptNode",
212
+ "module": [
213
+ "vellum",
214
+ "workflows",
215
+ "nodes",
216
+ "displayable",
217
+ "inline_prompt_node",
218
+ "node"
219
+ ]
220
+ },
221
+ "trigger": {
222
+ "id": "d14ff235-9e0a-4496-9fd4-ae4cf39fa518",
223
+ "merge_behavior": "AWAIT_ANY"
224
+ },
225
+ "ports": [
226
+ {
227
+ "id": "66943bf3-3c32-42da-9d4f-faab541f4285",
228
+ "name": "default",
229
+ "type": "DEFAULT"
230
+ }
231
+ ],
232
+ "outputs": [
233
+ {
234
+ "id": "af7d71b0-227c-4957-8234-d1dc76e62554",
235
+ "name": "json",
236
+ "type": "JSON",
237
+ "value": null
238
+ },
239
+ {
240
+ "id": "b283959e-b719-49d2-ab9a-15c804ab52e8",
241
+ "name": "text",
242
+ "type": "STRING",
243
+ "value": null
244
+ },
245
+ {
246
+ "id": "73c02172-2bd1-482d-b7fe-acc67135f7d0",
247
+ "name": "results",
248
+ "type": "ARRAY",
249
+ "value": null
250
+ }
251
+ ],
252
+ "attributes": [
253
+ {
254
+ "id": "e2455e77-82ad-460f-8735-10d427d4d448",
255
+ "name": "functions",
256
+ "value": {
257
+ "type": "CONSTANT_VALUE",
258
+ "value": {
259
+ "type": "JSON",
260
+ "value": null
261
+ }
262
+ }
263
+ },
264
+ {
265
+ "id": "bd29a2cd-4965-4741-a3c2-9d0c753efdd9",
266
+ "name": "parameters",
267
+ "value": {
268
+ "type": "CONSTANT_VALUE",
269
+ "value": {
270
+ "type": "JSON",
271
+ "value": {
272
+ "stop": [],
273
+ "temperature": 0.0,
274
+ "max_tokens": 4096.0,
275
+ "top_p": 1.0,
276
+ "top_k": 0.0,
277
+ "frequency_penalty": 0.0,
278
+ "presence_penalty": 0.0,
279
+ "logit_bias": null,
280
+ "custom_parameters": null
281
+ }
282
+ }
283
+ }
284
+ },
285
+ {
286
+ "id": "76f813d6-0025-4733-9d03-848c49c93d66",
287
+ "name": "ml_model",
288
+ "value": {
289
+ "type": "CONSTANT_VALUE",
290
+ "value": {
291
+ "type": "JSON",
292
+ "value": null
293
+ }
294
+ }
295
+ },
296
+ {
297
+ "id": "1216ae32-e051-4c48-bd7d-69f2a4527f12",
298
+ "name": "blocks",
299
+ "value": {
300
+ "type": "CONSTANT_VALUE",
301
+ "value": {
302
+ "type": "JSON",
303
+ "value": null
304
+ }
305
+ }
306
+ },
307
+ {
308
+ "id": "b66dc0b6-9e4d-4bbb-9b3c-b85e14071de0",
309
+ "name": "prompt_inputs",
310
+ "value": {
311
+ "type": "CONSTANT_VALUE",
312
+ "value": {
313
+ "type": "JSON",
314
+ "value": null
315
+ }
316
+ }
317
+ }
318
+ ]
319
+ },
320
+ {
321
+ "id": "9d62e15c-a640-4a65-b52c-ce0cbaa23a71",
322
+ "display_data": {
323
+ "position": {
324
+ "x": 0.0,
325
+ "y": 0.0
326
+ },
327
+ "comment": {
328
+ "value": "\n Used to execute a Subworkflow defined inline.\n\n subworkflow: Type[\"BaseWorkflow[InputsType, InnerStateType]\"] - The Subworkflow to execute\n subworkflow_inputs: ClassVar[EntityInputsInterface] = {}\n ",
329
+ "expanded": true
330
+ }
331
+ },
332
+ "base": {
333
+ "name": "BaseNode",
334
+ "module": [
335
+ "vellum",
336
+ "workflows",
337
+ "nodes",
338
+ "bases",
339
+ "base"
340
+ ]
341
+ },
342
+ "definition": {
343
+ "name": "InlineSubworkflowNode",
344
+ "module": [
345
+ "vellum",
346
+ "workflows",
347
+ "nodes",
348
+ "core",
349
+ "inline_subworkflow_node",
350
+ "node"
351
+ ]
352
+ },
353
+ "trigger": {
354
+ "id": "d7c92c2f-d761-43e7-a7e6-34fd74b3b8b3",
355
+ "merge_behavior": "AWAIT_ATTRIBUTES"
356
+ },
357
+ "ports": [
358
+ {
359
+ "id": "bb1e4a4f-60e7-4519-b127-80183008a3f7",
360
+ "name": "default",
361
+ "type": "DEFAULT"
362
+ }
363
+ ]
364
+ },
365
+ {
366
+ "id": "2dded52f-a449-48fd-b58f-f59801c24bae",
367
+ "display_data": {
368
+ "position": {
369
+ "x": 0.0,
370
+ "y": 0.0
371
+ },
372
+ "comment": {
373
+ "value": "\n Used to execute a Metric Definition and surface a float output representing the score.\n\n metric_definition: Union[UUID, str] - Either the Metric Definition's UUID or its name.\n metric_inputs: EntityInputsInterface - The inputs for the Metric\n release_tag: str - The release tag to use for the Metric\n request_options: Optional[RequestOptions] - The request options to use for the Metric\n ",
374
+ "expanded": true
375
+ }
376
+ },
377
+ "base": {
378
+ "name": "BaseNode",
379
+ "module": [
380
+ "vellum",
381
+ "workflows",
382
+ "nodes",
383
+ "bases",
384
+ "base"
385
+ ]
386
+ },
387
+ "definition": {
388
+ "name": "GuardrailNode",
389
+ "module": [
390
+ "vellum",
391
+ "workflows",
392
+ "nodes",
393
+ "displayable",
394
+ "guardrail_node",
395
+ "node"
396
+ ]
397
+ },
398
+ "trigger": {
399
+ "id": "1560db25-a416-4ce6-b175-d7bdfbdb27d8",
400
+ "merge_behavior": "AWAIT_ANY"
401
+ },
402
+ "ports": [
403
+ {
404
+ "id": "94bc1403-af9f-4539-8208-1bbafbf09564",
405
+ "name": "default",
406
+ "type": "DEFAULT"
407
+ }
408
+ ]
409
+ },
410
+ {
411
+ "id": "527a10bc-30b2-4e36-91d6-91c8e865bac8",
412
+ "display_data": {
413
+ "position": {
414
+ "x": 0.0,
415
+ "y": 0.0
416
+ },
417
+ "comment": {
418
+ "value": "\n Used to map over a list of items and execute a Subworkflow on each iteration.\n\n items: List[MapNodeItemType] - The items to map over\n max_concurrency: Optional[int] = None - The maximum number of concurrent subworkflow executions\n subworkflow: Type[\"BaseWorkflow\"] - The Subworkflow to execute\n ",
419
+ "expanded": true
420
+ }
421
+ },
422
+ "base": {
423
+ "name": "BaseAdornmentNode",
424
+ "module": [
425
+ "vellum",
426
+ "workflows",
427
+ "nodes",
428
+ "bases",
429
+ "base_adornment_node"
430
+ ]
431
+ },
432
+ "definition": {
433
+ "name": "MapNode",
434
+ "module": [
435
+ "vellum",
436
+ "workflows",
437
+ "nodes",
438
+ "core",
439
+ "map_node",
440
+ "node"
441
+ ]
442
+ },
443
+ "trigger": {
444
+ "id": "3a702aac-7b68-47d3-92e1-264883e9532c",
445
+ "merge_behavior": "AWAIT_ATTRIBUTES"
446
+ },
447
+ "ports": [
448
+ {
449
+ "id": "0ae85239-a159-48af-b39c-2484eac901df",
450
+ "name": "default",
451
+ "type": "DEFAULT"
452
+ }
453
+ ]
454
+ },
455
+ {
456
+ "id": "7426f273-a43d-4448-a2d2-76d0ee0d069c",
457
+ "display_data": {
458
+ "position": {
459
+ "x": 0.0,
460
+ "y": 0.0
461
+ },
462
+ "comment": {
463
+ "value": "\n Used to merge the control flow of multiple nodes into a single node. This node exists to be backwards compatible\n with Vellum's Merge Node, and for most cases, you should extend from `BaseNode.Trigger` directly.\n ",
464
+ "expanded": true
465
+ }
466
+ },
467
+ "base": {
468
+ "name": "BaseNode",
469
+ "module": [
470
+ "vellum",
471
+ "workflows",
472
+ "nodes",
473
+ "bases",
474
+ "base"
475
+ ]
476
+ },
477
+ "definition": {
478
+ "name": "MergeNode",
479
+ "module": [
480
+ "vellum",
481
+ "workflows",
482
+ "nodes",
483
+ "displayable",
484
+ "merge_node",
485
+ "node"
486
+ ]
487
+ },
488
+ "trigger": {
489
+ "id": "3a91ba25-0dd0-4bc8-9f6d-c54ea4dccf64",
490
+ "merge_behavior": "AWAIT_ANY"
491
+ },
492
+ "ports": [
493
+ {
494
+ "id": "50515fe3-4be8-43b9-8fc5-d52c1250115e",
495
+ "name": "default",
496
+ "type": "DEFAULT"
497
+ }
498
+ ]
499
+ },
81
500
  {
82
501
  "id": "16b8d82d-458d-48d9-b57e-a447b19f311a",
83
502
  "display_data": {
@@ -435,49 +854,21 @@
435
854
  }
436
855
  ],
437
856
  "errors": [
438
- {
439
- "node": "APINode",
440
- "error": "APINode.Outputs.text"
441
- },
442
- {
443
- "node": "CodeExecutionNode",
444
- "error": "vellum.workflows.nodes.displayable.code_execution_node.node.CodeExecutionNode.Ports.default"
445
- },
446
- {
447
- "node": "InlinePromptNode",
448
- "error": "vellum.workflows.nodes.displayable.inline_prompt_node.node.InlinePromptNode.Ports.default"
449
- },
450
- {
451
- "node": "InlineSubworkflowNode",
452
- "error": "'NoneType' object has no attribute 'get_inputs_class'"
453
- },
454
- {
455
- "node": "GuardrailNode",
456
- "error": "vellum.workflows.nodes.displayable.guardrail_node.node.GuardrailNode.Ports.default"
457
- },
458
- {
459
- "node": "MapNode",
460
- "error": "Expected NodeReference items to have an instance"
461
- },
462
- {
463
- "node": "MergeNode",
464
- "error": "vellum.workflows.nodes.displayable.merge_node.node.MergeNode.Ports.default"
465
- },
466
857
  {
467
858
  "node": "SubworkflowDeploymentNode",
468
- "error": "headers: {'server': 'gunicorn', 'date': 'Mon, 22 Sep 2025 14:24:17 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '28', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Invalid API key'}"
859
+ "error": "ApiError: headers: {'server': 'gunicorn', 'date': 'Wed, 24 Sep 2025 04:58:53 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '58', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Authentication credentials were not provided.'}"
469
860
  },
470
861
  {
471
862
  "node": "PromptDeploymentNode",
472
- "error": "headers: {'server': 'gunicorn', 'date': 'Mon, 22 Sep 2025 14:24:17 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '28', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Invalid API key'}"
863
+ "error": "ApiError: headers: {'server': 'gunicorn', 'date': 'Wed, 24 Sep 2025 04:58:53 GMT', 'content-type': 'application/json', 'allow': 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS', 'x-frame-options': 'DENY', 'content-length': '58', 'vary': 'Accept-Language, Origin', 'content-language': 'en', 'strict-transport-security': 'max-age=60; includeSubDomains; preload', 'x-content-type-options': 'nosniff', 'referrer-policy': 'same-origin', 'cross-origin-opener-policy': 'same-origin', 'via': '1.1 google', 'alt-svc': 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000'}, status_code: 403, body: {'detail': 'Authentication credentials were not provided.'}"
473
864
  },
474
865
  {
475
866
  "node": "SearchNode",
476
- "error": "Expected NodeReference query to have an instance"
867
+ "error": "ValueError: Expected NodeReference query to have an instance"
477
868
  },
478
869
  {
479
870
  "node": "TemplatingNode",
480
- "error": "TemplatingNode.Outputs.result"
871
+ "error": "KeyError: TemplatingNode.Outputs.result"
481
872
  }
482
873
  ]
483
874
  }
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
15
15
  def create_display_context_with_client() -> WorkflowDisplayContext:
16
16
  """Create a WorkflowDisplayContext with Vellum client for serialization."""
17
17
  client = create_vellum_client()
18
- return WorkflowDisplayContext(client=client)
18
+ return WorkflowDisplayContext(client=client, dry_run=True)
19
19
 
20
20
 
21
21
  def get_all_displayable_node_classes() -> List[Type[BaseNode]]:
@@ -70,7 +70,7 @@ def main() -> None:
70
70
  display_instance = display_class()
71
71
  display_instance.serialize(display_context)
72
72
  except Exception as e:
73
- errors.append({"node": node_class.__name__, "error": str(e)})
73
+ errors.append({"node": node_class.__name__, "error": f"{e.__class__.__name__}: {str(e)}"})
74
74
 
75
75
  result = {"nodes": successful_nodes, "errors": errors}
76
76
 
@@ -323,12 +323,12 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
323
323
  )
324
324
  return node_definition
325
325
 
326
- def get_node_output_display(self, output: OutputReference) -> Tuple[Type[BaseNode], NodeOutputDisplay]:
326
+ def get_node_output_display(self, output: OutputReference) -> NodeOutputDisplay:
327
327
  explicit_display = self.output_display.get(output)
328
328
  if explicit_display:
329
- return self._node, explicit_display
329
+ return explicit_display
330
330
 
331
- return (self._node, NodeOutputDisplay(id=uuid4_from_hash(f"{self.node_id}|{output.name}"), name=output.name))
331
+ return NodeOutputDisplay(id=uuid4_from_hash(f"{self.node_id}|{output.name}"), name=output.name)
332
332
 
333
333
  def get_node_port_display(self, port: Port) -> PortDisplay:
334
334
  overrides = self.port_displays.get(port)
@@ -347,6 +347,9 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
347
347
  if default_port in port_displays:
348
348
  return port_displays[default_port].id
349
349
 
350
+ if default_port:
351
+ return self.get_node_port_display(default_port).id
352
+
350
353
  first_port = next((port for port in unadorned_node.Ports), None)
351
354
  if not first_port:
352
355
  raise ValueError(f"Node {self._node.__name__} must have at least one port.")
@@ -1,8 +1,7 @@
1
1
  from uuid import UUID
2
- from typing import ClassVar, Dict, Generic, Optional, TypeVar, cast
2
+ from typing import ClassVar, Dict, Generic, Optional, TypeVar
3
3
 
4
4
  from vellum.workflows.nodes.displayable import APINode
5
- from vellum.workflows.references.output import OutputReference
6
5
  from vellum.workflows.types.core import JsonArray, JsonObject
7
6
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
8
7
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
@@ -178,11 +177,9 @@ class BaseAPINodeDisplay(BaseNodeDisplay[_APINodeType], Generic[_APINodeType]):
178
177
  ]
179
178
  inputs.extend(additional_header_inputs)
180
179
 
181
- _, text_output_display = display_context.global_node_output_displays[cast(OutputReference, node.Outputs.text)]
182
- _, json_output_display = display_context.global_node_output_displays[cast(OutputReference, node.Outputs.json)]
183
- _, status_code_output_display = display_context.global_node_output_displays[
184
- cast(OutputReference, node.Outputs.status_code)
185
- ]
180
+ text_output_display = self.get_node_output_display(node.Outputs.text)
181
+ json_output_display = self.get_node_output_display(node.Outputs.json)
182
+ status_code_output_display = self.get_node_output_display(node.Outputs.status_code)
186
183
 
187
184
  serialized_node: JsonObject = {
188
185
  "id": str(node_id),
@@ -7,6 +7,8 @@ from vellum.workflows.inputs.base import BaseInputs
7
7
  from vellum.workflows.nodes import InlineSubworkflowNode
8
8
  from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
9
9
  from vellum.workflows.types.core import JsonObject
10
+ from vellum.workflows.workflows.base import BaseWorkflow
11
+ from vellum_ee.workflows.display.exceptions import NodeValidationError
10
12
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
11
13
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
12
14
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -31,14 +33,25 @@ class BaseInlineSubworkflowNodeDisplay(
31
33
  node = self._node
32
34
  node_id = self.node_id
33
35
 
34
- node_inputs, workflow_inputs = self._generate_node_and_workflow_inputs(node_id, node, display_context)
36
+ subworkflow_class = raise_if_descriptor(node.subworkflow)
37
+ if subworkflow_class is None:
38
+ display_context.add_error(
39
+ NodeValidationError(
40
+ "InlineSubworkflowNode requires a subworkflow to be defined",
41
+ node_class_name=node.__class__.__name__,
42
+ )
43
+ )
44
+ subworkflow_class = BaseWorkflow
35
45
 
46
+ node_inputs, workflow_inputs = self._generate_node_and_workflow_inputs(
47
+ node_id, node, display_context, subworkflow_class
48
+ )
36
49
  subworkflow_display = get_workflow_display(
37
50
  base_display_class=display_context.workflow_display_class,
38
- workflow_class=raise_if_descriptor(node.subworkflow),
51
+ workflow_class=subworkflow_class,
39
52
  parent_display_context=display_context,
40
53
  )
41
- workflow_outputs = self._generate_workflow_outputs(node, subworkflow_display.display_context)
54
+ workflow_outputs = self._generate_workflow_outputs(node, subworkflow_display.display_context, subworkflow_class)
42
55
  serialized_subworkflow = subworkflow_display.serialize()
43
56
 
44
57
  return {
@@ -63,8 +76,8 @@ class BaseInlineSubworkflowNodeDisplay(
63
76
  node_id: UUID,
64
77
  node: Type[InlineSubworkflowNode],
65
78
  display_context: WorkflowDisplayContext,
79
+ subworkflow: Type[BaseWorkflow],
66
80
  ) -> Tuple[List[NodeInput], List[VellumVariable]]:
67
- subworkflow = raise_if_descriptor(node.subworkflow)
68
81
  subworkflow_inputs_class = subworkflow.get_inputs_class()
69
82
  subworkflow_inputs = raise_if_descriptor(node.subworkflow_inputs)
70
83
 
@@ -115,9 +128,10 @@ class BaseInlineSubworkflowNodeDisplay(
115
128
  self,
116
129
  node: Type[InlineSubworkflowNode],
117
130
  display_context: WorkflowDisplayContext,
131
+ subworkflow: Type[BaseWorkflow],
118
132
  ) -> List[VellumVariable]:
119
133
  workflow_outputs: List[VellumVariable] = []
120
- for output_descriptor in raise_if_descriptor(node.subworkflow).Outputs: # type: ignore[union-attr]
134
+ for output_descriptor in subworkflow.Outputs: # type: ignore[union-attr]
121
135
  workflow_output_display = display_context.workflow_output_displays[output_descriptor]
122
136
  output_type = infer_vellum_variable_type(output_descriptor)
123
137
  workflow_outputs.append(
@@ -1,7 +1,8 @@
1
1
  from uuid import UUID
2
- from typing import Generic, Optional, TypeVar, cast
2
+ from typing import Generic, Optional, Type, TypeVar, cast
3
3
 
4
4
  from vellum.workflows.nodes import MapNode
5
+ from vellum.workflows.state.base import BaseState
5
6
  from vellum.workflows.types.core import JsonObject
6
7
  from vellum.workflows.workflows.base import BaseWorkflow
7
8
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
@@ -22,12 +23,19 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
22
23
  node = self._node
23
24
  node_id = self.node_id
24
25
 
25
- subworkflow = cast(type[BaseWorkflow], raise_if_descriptor(node.subworkflow))
26
+ subworkflow_value = raise_if_descriptor(node.subworkflow)
27
+ subworkflow = (
28
+ cast(type[BaseWorkflow], subworkflow_value)
29
+ if subworkflow_value is not None
30
+ else self._default_workflow_class()
31
+ )
32
+
33
+ items = raise_if_descriptor(node.items)
26
34
 
27
35
  items_node_input = create_node_input(
28
36
  node_id=node_id,
29
37
  input_name="items",
30
- value=node.items,
38
+ value=items or [],
31
39
  display_context=display_context,
32
40
  input_id=self.node_input_ids_by_name.get("items"),
33
41
  )
@@ -81,3 +89,9 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
81
89
  },
82
90
  **self.serialize_generic_fields(display_context),
83
91
  }
92
+
93
+ def _default_workflow_class(self) -> Type[BaseWorkflow]:
94
+ class MapNodeSubworkflow(BaseWorkflow[MapNode.SubworkflowInputs, BaseState]):
95
+ pass
96
+
97
+ return MapNodeSubworkflow
@@ -1,7 +1,6 @@
1
1
  import inspect
2
- from typing import Any, Generic, Tuple, Type, TypeVar
2
+ from typing import Any, Generic, TypeVar
3
3
 
4
- from vellum.workflows.nodes.bases.base import BaseNode
5
4
  from vellum.workflows.nodes.core.retry_node.node import RetryNode
6
5
  from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
7
6
  from vellum.workflows.references.output import OutputReference
@@ -66,7 +65,7 @@ class BaseRetryNodeDisplay(BaseAdornmentNodeDisplay[_RetryNodeType], Generic[_Re
66
65
 
67
66
  return serialized_node
68
67
 
69
- def get_node_output_display(self, output: OutputReference) -> Tuple[Type[BaseNode], NodeOutputDisplay]:
68
+ def get_node_output_display(self, output: OutputReference) -> NodeOutputDisplay:
70
69
  inner_node = self._node.__wrapped_node__
71
70
  if not inner_node:
72
71
  return super().get_node_output_display(output)