vellum-ai 1.4.1__py3-none-any.whl → 1.5.0__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 (64) hide show
  1. vellum/__init__.py +14 -0
  2. vellum/client/__init__.py +3 -0
  3. vellum/client/core/client_wrapper.py +2 -2
  4. vellum/client/reference.md +160 -0
  5. vellum/client/resources/__init__.py +2 -0
  6. vellum/client/resources/integrations/__init__.py +4 -0
  7. vellum/client/resources/integrations/client.py +260 -0
  8. vellum/client/resources/integrations/raw_client.py +267 -0
  9. vellum/client/types/__init__.py +12 -0
  10. vellum/client/types/components_schemas_composio_execute_tool_request.py +5 -0
  11. vellum/client/types/components_schemas_composio_execute_tool_response.py +5 -0
  12. vellum/client/types/components_schemas_composio_tool_definition.py +5 -0
  13. vellum/client/types/composio_execute_tool_request.py +24 -0
  14. vellum/client/types/composio_execute_tool_response.py +24 -0
  15. vellum/client/types/composio_tool_definition.py +26 -0
  16. vellum/client/types/vellum_error_code_enum.py +2 -0
  17. vellum/client/types/vellum_sdk_error.py +1 -0
  18. vellum/client/types/workflow_event_error.py +1 -0
  19. vellum/resources/integrations/__init__.py +3 -0
  20. vellum/resources/integrations/client.py +3 -0
  21. vellum/resources/integrations/raw_client.py +3 -0
  22. vellum/types/components_schemas_composio_execute_tool_request.py +3 -0
  23. vellum/types/components_schemas_composio_execute_tool_response.py +3 -0
  24. vellum/types/components_schemas_composio_tool_definition.py +3 -0
  25. vellum/types/composio_execute_tool_request.py +3 -0
  26. vellum/types/composio_execute_tool_response.py +3 -0
  27. vellum/types/composio_tool_definition.py +3 -0
  28. vellum/workflows/constants.py +4 -0
  29. vellum/workflows/emitters/base.py +8 -0
  30. vellum/workflows/emitters/vellum_emitter.py +10 -0
  31. vellum/workflows/inputs/dataset_row.py +2 -2
  32. vellum/workflows/nodes/bases/base.py +12 -1
  33. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +6 -0
  34. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +16 -2
  35. vellum/workflows/nodes/displayable/final_output_node/node.py +59 -0
  36. vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +40 -1
  37. vellum/workflows/nodes/displayable/tool_calling_node/node.py +3 -0
  38. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py +64 -0
  39. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +30 -41
  40. vellum/workflows/runner/runner.py +132 -110
  41. vellum/workflows/tests/test_dataset_row.py +29 -0
  42. vellum/workflows/types/core.py +13 -2
  43. vellum/workflows/types/definition.py +13 -1
  44. vellum/workflows/utils/functions.py +69 -27
  45. vellum/workflows/utils/tests/test_functions.py +50 -6
  46. vellum/workflows/vellum_client.py +7 -1
  47. vellum/workflows/workflows/base.py +26 -4
  48. vellum/workflows/workflows/tests/test_base_workflow.py +54 -0
  49. {vellum_ai-1.4.1.dist-info → vellum_ai-1.5.0.dist-info}/METADATA +1 -1
  50. {vellum_ai-1.4.1.dist-info → vellum_ai-1.5.0.dist-info}/RECORD +63 -42
  51. vellum_ai-1.5.0.dist-info/entry_points.txt +4 -0
  52. vellum_cli/tests/test_pull.py +1 -0
  53. vellum_cli/tests/test_push.py +2 -0
  54. vellum_ee/assets/node-definitions.json +483 -0
  55. vellum_ee/scripts/generate_node_definitions.py +89 -0
  56. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -3
  57. vellum_ee/workflows/display/nodes/vellum/tests/test_final_output_node.py +78 -0
  58. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +5 -0
  59. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +5 -0
  60. vellum_ee/workflows/display/types.py +3 -0
  61. vellum_ee/workflows/display/workflows/base_workflow_display.py +6 -0
  62. vellum_ai-1.4.1.dist-info/entry_points.txt +0 -3
  63. {vellum_ai-1.4.1.dist-info → vellum_ai-1.5.0.dist-info}/LICENSE +0 -0
  64. {vellum_ai-1.4.1.dist-info → vellum_ai-1.5.0.dist-info}/WHEEL +0 -0
@@ -721,6 +721,7 @@ MY_OTHER_VELLUM_API_KEY=aaabbbcccddd
721
721
  vellum_client_class.assert_called_once_with(
722
722
  api_key="aaabbbcccddd",
723
723
  environment=mock.ANY,
724
+ api_version=None,
724
725
  )
725
726
 
726
727
  # AND the vellum lock file should have been updated with the correct workspace
@@ -882,6 +883,7 @@ MY_OTHER_VELLUM_API_KEY=aaabbbcccddd
882
883
  vellum_client_class.assert_called_once_with(
883
884
  api_key="aaabbbcccddd",
884
885
  environment=mock.ANY,
886
+ api_version=None,
885
887
  )
886
888
 
887
889
  # AND the vellum lock file should have the same two workflows
@@ -0,0 +1,483 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "id": "5d3c60ce-7acf-4d8b-a68d-65cc0b561ac5",
5
+ "display_data": {
6
+ "position": {
7
+ "x": 0.0,
8
+ "y": 0.0
9
+ },
10
+ "comment": {
11
+ "value": "\n Used to conditionally determine which port to invoke next. This node exists to be backwards compatible with\n Vellum's Conditional Node, and for most cases, you should extend `BaseNode.Ports` directly.\n ",
12
+ "expanded": true
13
+ }
14
+ },
15
+ "base": {
16
+ "name": "BaseNode",
17
+ "module": [
18
+ "vellum",
19
+ "workflows",
20
+ "nodes",
21
+ "bases",
22
+ "base"
23
+ ]
24
+ },
25
+ "definition": {
26
+ "name": "ConditionalNode",
27
+ "module": [
28
+ "vellum",
29
+ "workflows",
30
+ "nodes",
31
+ "displayable",
32
+ "conditional_node",
33
+ "node"
34
+ ]
35
+ },
36
+ "trigger": {
37
+ "id": "2b5617b4-5cc0-4f9a-9369-24982e2cd8c0",
38
+ "merge_behavior": "AWAIT_ANY"
39
+ },
40
+ "ports": []
41
+ },
42
+ {
43
+ "id": "92e401f1-110f-4edc-8bb0-cad879e1ea08",
44
+ "display_data": {
45
+ "position": {
46
+ "x": 0.0,
47
+ "y": 0.0
48
+ },
49
+ "comment": {
50
+ "value": "\n Used to raise an error to reject the surrounding Workflow.\n\n error: Union[str, VellumError] - The error to raise.\n ",
51
+ "expanded": true
52
+ }
53
+ },
54
+ "base": {
55
+ "name": "BaseNode",
56
+ "module": [
57
+ "vellum",
58
+ "workflows",
59
+ "nodes",
60
+ "bases",
61
+ "base"
62
+ ]
63
+ },
64
+ "definition": {
65
+ "name": "ErrorNode",
66
+ "module": [
67
+ "vellum",
68
+ "workflows",
69
+ "nodes",
70
+ "core",
71
+ "error_node",
72
+ "node"
73
+ ]
74
+ },
75
+ "trigger": {
76
+ "id": "c1b30829-76af-4d99-bf83-b030c551a7cf",
77
+ "merge_behavior": "AWAIT_ATTRIBUTES"
78
+ },
79
+ "ports": []
80
+ },
81
+ {
82
+ "id": "16b8d82d-458d-48d9-b57e-a447b19f311a",
83
+ "display_data": {
84
+ "position": {
85
+ "x": 0.0,
86
+ "y": 0.0
87
+ },
88
+ "comment": {
89
+ "value": "\n A no-op Node purely used to display a note in the Vellum UI.\n ",
90
+ "expanded": true
91
+ }
92
+ },
93
+ "base": {
94
+ "name": "BaseNode",
95
+ "module": [
96
+ "vellum",
97
+ "workflows",
98
+ "nodes",
99
+ "bases",
100
+ "base"
101
+ ]
102
+ },
103
+ "definition": {
104
+ "name": "NoteNode",
105
+ "module": [
106
+ "vellum",
107
+ "workflows",
108
+ "nodes",
109
+ "displayable",
110
+ "note_node",
111
+ "node"
112
+ ]
113
+ },
114
+ "trigger": {
115
+ "id": "2b6907b0-1adc-41e7-9d5b-3c6ccd9b973a",
116
+ "merge_behavior": "AWAIT_ANY"
117
+ },
118
+ "ports": [
119
+ {
120
+ "id": "22dac74c-7ad6-4a6f-97ae-0034124721ce",
121
+ "name": "default",
122
+ "type": "DEFAULT"
123
+ }
124
+ ]
125
+ },
126
+ {
127
+ "id": "035842e0-34ed-43af-97de-a706e40912ae",
128
+ "label": "Tool Calling Node",
129
+ "display_data": {
130
+ "position": {
131
+ "x": 0.0,
132
+ "y": 0.0
133
+ },
134
+ "comment": {
135
+ "value": "\n A Node that dynamically invokes the provided functions to the underlying Prompt\n\n Attributes:\n ml_model: str - The model to use for tool calling (e.g., \"gpt-4o-mini\")\n blocks: List[PromptBlock] - The prompt blocks to use (same format as InlinePromptNode)\n functions: List[Tool] - The functions that can be called\n prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values\n parameters: PromptParameters - The parameters for the Prompt\n max_prompt_iterations: Optional[int] - Maximum number of prompt iterations before stopping\n ",
136
+ "expanded": true
137
+ }
138
+ },
139
+ "base": {
140
+ "name": "BaseNode",
141
+ "module": [
142
+ "vellum",
143
+ "workflows",
144
+ "nodes",
145
+ "bases",
146
+ "base"
147
+ ]
148
+ },
149
+ "definition": {
150
+ "name": "ToolCallingNode",
151
+ "module": [
152
+ "vellum",
153
+ "workflows",
154
+ "nodes",
155
+ "displayable",
156
+ "tool_calling_node",
157
+ "node"
158
+ ]
159
+ },
160
+ "trigger": {
161
+ "id": "f9b4a4ce-68ae-45f4-9e29-38b588abdf97",
162
+ "merge_behavior": "AWAIT_ATTRIBUTES"
163
+ },
164
+ "ports": [
165
+ {
166
+ "id": "72157fce-fc16-44d8-a00d-506738a20730",
167
+ "name": "default",
168
+ "type": "DEFAULT"
169
+ }
170
+ ],
171
+ "attributes": [
172
+ {
173
+ "id": "df3761db-067a-4fe9-9fc3-ba270786fcf6",
174
+ "name": "ml_model",
175
+ "value": {
176
+ "type": "CONSTANT_VALUE",
177
+ "value": {
178
+ "type": "STRING",
179
+ "value": "gpt-4o-mini"
180
+ }
181
+ }
182
+ },
183
+ {
184
+ "id": "66133945-f7fd-43bb-8b46-0761e2b58241",
185
+ "name": "blocks",
186
+ "value": {
187
+ "type": "CONSTANT_VALUE",
188
+ "value": {
189
+ "type": "JSON",
190
+ "value": []
191
+ }
192
+ }
193
+ },
194
+ {
195
+ "id": "0079b429-3b1c-4f7c-9ccf-185ace4dcbb2",
196
+ "name": "functions",
197
+ "value": {
198
+ "type": "CONSTANT_VALUE",
199
+ "value": {
200
+ "type": "JSON",
201
+ "value": []
202
+ }
203
+ }
204
+ },
205
+ {
206
+ "id": "ebda56e6-2ace-472c-a106-36cb69264e96",
207
+ "name": "prompt_inputs",
208
+ "value": {
209
+ "type": "CONSTANT_VALUE",
210
+ "value": {
211
+ "type": "JSON",
212
+ "value": null
213
+ }
214
+ }
215
+ },
216
+ {
217
+ "id": "2534a586-5b37-41f3-a9a4-8162b8538df4",
218
+ "name": "parameters",
219
+ "value": {
220
+ "type": "CONSTANT_VALUE",
221
+ "value": {
222
+ "type": "JSON",
223
+ "value": {
224
+ "stop": [],
225
+ "temperature": 0.0,
226
+ "max_tokens": 4096.0,
227
+ "top_p": 1.0,
228
+ "top_k": 0.0,
229
+ "frequency_penalty": 0.0,
230
+ "presence_penalty": 0.0,
231
+ "logit_bias": null,
232
+ "custom_parameters": null
233
+ }
234
+ }
235
+ }
236
+ },
237
+ {
238
+ "id": "118ef72a-0767-4659-8ddf-58a071f95988",
239
+ "name": "max_prompt_iterations",
240
+ "value": {
241
+ "type": "CONSTANT_VALUE",
242
+ "value": {
243
+ "type": "NUMBER",
244
+ "value": 5.0
245
+ }
246
+ }
247
+ },
248
+ {
249
+ "id": "d7ef5296-1e48-4d05-b97a-77f16d46fd50",
250
+ "name": "settings",
251
+ "value": {
252
+ "type": "CONSTANT_VALUE",
253
+ "value": {
254
+ "type": "JSON",
255
+ "value": null
256
+ }
257
+ }
258
+ }
259
+ ],
260
+ "outputs": [
261
+ {
262
+ "id": "c18a8725-21e1-4736-a77c-76d0fc035176",
263
+ "name": "text",
264
+ "type": "STRING",
265
+ "value": null
266
+ },
267
+ {
268
+ "id": "5a9ad073-c670-43d2-9198-a08e9fdda7ad",
269
+ "name": "chat_history",
270
+ "type": "CHAT_HISTORY",
271
+ "value": null
272
+ }
273
+ ]
274
+ },
275
+ {
276
+ "id": "187ed9ed-ca2b-49e4-943d-ee8da5fad973",
277
+ "label": "Web Search Node",
278
+ "display_data": {
279
+ "position": {
280
+ "x": 0.0,
281
+ "y": 0.0
282
+ },
283
+ "comment": {
284
+ "value": "\n Used to perform web search using SerpAPI.\n\n query: str - The search query to execute\n api_key: str - SerpAPI authentication key\n num_results: int - Number of search results to return (default: 10)\n location: Optional[str] - Geographic location filter for search\n ",
285
+ "expanded": true
286
+ }
287
+ },
288
+ "base": {
289
+ "name": "BaseNode",
290
+ "module": [
291
+ "vellum",
292
+ "workflows",
293
+ "nodes",
294
+ "bases",
295
+ "base"
296
+ ]
297
+ },
298
+ "definition": {
299
+ "name": "WebSearchNode",
300
+ "module": [
301
+ "vellum",
302
+ "workflows",
303
+ "nodes",
304
+ "displayable",
305
+ "web_search_node",
306
+ "node"
307
+ ]
308
+ },
309
+ "trigger": {
310
+ "id": "036c7a0c-fff3-44f7-b49b-429d8f44f212",
311
+ "merge_behavior": "AWAIT_ATTRIBUTES"
312
+ },
313
+ "ports": [
314
+ {
315
+ "id": "ef824748-d00d-41e7-b7b4-d0edc536c3af",
316
+ "name": "default",
317
+ "type": "DEFAULT"
318
+ }
319
+ ],
320
+ "attributes": [
321
+ {
322
+ "id": "b5ba3c60-e02c-46c8-b470-d86c4f8f42af",
323
+ "name": "query",
324
+ "value": {
325
+ "type": "CONSTANT_VALUE",
326
+ "value": {
327
+ "type": "STRING",
328
+ "value": ""
329
+ }
330
+ }
331
+ },
332
+ {
333
+ "id": "222408bc-7dae-4bb8-9aba-a1ac36bdc759",
334
+ "name": "api_key",
335
+ "value": {
336
+ "type": "CONSTANT_VALUE",
337
+ "value": {
338
+ "type": "JSON",
339
+ "value": null
340
+ }
341
+ }
342
+ },
343
+ {
344
+ "id": "795a0bbc-bb98-4bc8-a9d9-88b0cb796d23",
345
+ "name": "num_results",
346
+ "value": {
347
+ "type": "CONSTANT_VALUE",
348
+ "value": {
349
+ "type": "NUMBER",
350
+ "value": 10.0
351
+ }
352
+ }
353
+ },
354
+ {
355
+ "id": "e1648557-ca03-4a81-ab5b-86495284b325",
356
+ "name": "location",
357
+ "value": {
358
+ "type": "CONSTANT_VALUE",
359
+ "value": {
360
+ "type": "JSON",
361
+ "value": null
362
+ }
363
+ }
364
+ }
365
+ ],
366
+ "outputs": [
367
+ {
368
+ "id": "704a4ba3-1afe-482d-876a-91dcdbe90fb7",
369
+ "name": "text",
370
+ "type": "STRING",
371
+ "value": null
372
+ },
373
+ {
374
+ "id": "574b2ebe-f250-4024-b517-3cd47704c70e",
375
+ "name": "urls",
376
+ "type": "JSON",
377
+ "value": null
378
+ },
379
+ {
380
+ "id": "9798a067-3589-4a2a-a811-6019873cbbfe",
381
+ "name": "results",
382
+ "type": "JSON",
383
+ "value": null
384
+ }
385
+ ]
386
+ },
387
+ {
388
+ "id": "bf31cc42-b5e4-4191-a453-7191743ac200",
389
+ "display_data": {
390
+ "position": {
391
+ "x": 0.0,
392
+ "y": 0.0
393
+ },
394
+ "comment": {
395
+ "value": "\n Used to directly reference the output of another node.\n This provides backward compatibility with Vellum's Final Output Node.\n ",
396
+ "expanded": true
397
+ }
398
+ },
399
+ "base": {
400
+ "name": "BaseNode",
401
+ "module": [
402
+ "vellum",
403
+ "workflows",
404
+ "nodes",
405
+ "bases",
406
+ "base"
407
+ ]
408
+ },
409
+ "definition": {
410
+ "name": "FinalOutputNode",
411
+ "module": [
412
+ "vellum",
413
+ "workflows",
414
+ "nodes",
415
+ "displayable",
416
+ "final_output_node",
417
+ "node"
418
+ ]
419
+ },
420
+ "trigger": {
421
+ "id": "0b2da045-2a4c-4939-8b5c-661652c759d4",
422
+ "merge_behavior": "AWAIT_ANY"
423
+ },
424
+ "ports": [],
425
+ "outputs": [
426
+ {
427
+ "id": "7f41909e-9ce1-4fde-ba77-a13dda301ece",
428
+ "name": "value",
429
+ "type": "STRING",
430
+ "value": {
431
+ "__undefined__": true
432
+ }
433
+ }
434
+ ]
435
+ }
436
+ ],
437
+ "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
+ {
467
+ "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'}"
469
+ },
470
+ {
471
+ "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'}"
473
+ },
474
+ {
475
+ "node": "SearchNode",
476
+ "error": "Expected NodeReference query to have an instance"
477
+ },
478
+ {
479
+ "node": "TemplatingNode",
480
+ "error": "TemplatingNode.Outputs.result"
481
+ }
482
+ ]
483
+ }
@@ -0,0 +1,89 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from typing import Any, Dict, List, Optional, Type
5
+
6
+ from vellum.workflows.nodes.bases.base import BaseNode
7
+ import vellum.workflows.nodes.displayable as displayable_module
8
+ from vellum.workflows.vellum_client import create_vellum_client
9
+ from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
10
+ from vellum_ee.workflows.display.types import WorkflowDisplayContext
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def create_display_context_with_client() -> WorkflowDisplayContext:
16
+ """Create a WorkflowDisplayContext with Vellum client for serialization."""
17
+ client = create_vellum_client()
18
+ return WorkflowDisplayContext(client=client)
19
+
20
+
21
+ def get_all_displayable_node_classes() -> List[Type[BaseNode]]:
22
+ """Get all displayable node classes dynamically from the displayable module."""
23
+ node_classes = []
24
+ for class_name in displayable_module.__all__:
25
+ node_class = getattr(displayable_module, class_name)
26
+ node_classes.append(node_class)
27
+ return node_classes
28
+
29
+
30
+ def clean_node_definition(definition: Dict[str, Any]) -> Dict[str, Any]:
31
+ """Remove unwanted fields from a successfully serialized node definition."""
32
+ fields_to_remove = ["inputs", "data", "type", "adornments", "should_file_merge"]
33
+ cleaned = {k: v for k, v in definition.items() if k not in fields_to_remove}
34
+ return cleaned
35
+
36
+
37
+ def serialize_node_definition(
38
+ node_class: Type[BaseNode], display_context: WorkflowDisplayContext
39
+ ) -> Optional[Dict[str, Any]]:
40
+ """Serialize a single node definition, returning None if it fails."""
41
+ try:
42
+ display_class = get_node_display_class(node_class)
43
+ display_instance = display_class()
44
+ definition = display_instance.serialize(display_context)
45
+ return clean_node_definition(definition)
46
+ except Exception as e:
47
+ logger.info(f"Warning: Failed to serialize {node_class.__name__}: {e}")
48
+ return None
49
+
50
+
51
+ def main() -> None:
52
+ """Main function to generate node definitions."""
53
+ logger.info("Generating node definitions...")
54
+
55
+ display_context = create_display_context_with_client()
56
+ node_classes = get_all_displayable_node_classes()
57
+
58
+ successful_nodes = []
59
+ errors = []
60
+
61
+ for node_class in node_classes:
62
+ logger.info(f"Serializing {node_class.__name__}...")
63
+ definition = serialize_node_definition(node_class, display_context)
64
+
65
+ if definition is not None:
66
+ successful_nodes.append(definition)
67
+ else:
68
+ try:
69
+ display_class = get_node_display_class(node_class)
70
+ display_instance = display_class()
71
+ display_instance.serialize(display_context)
72
+ except Exception as e:
73
+ errors.append({"node": node_class.__name__, "error": str(e)})
74
+
75
+ result = {"nodes": successful_nodes, "errors": errors}
76
+
77
+ output_path = "ee/vellum_ee/assets/node-definitions.json"
78
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
79
+
80
+ with open(output_path, "w") as f:
81
+ json.dump(result, f, indent=2)
82
+
83
+ logger.info(
84
+ f"Generated {len(successful_nodes)} successful node definitions and {len(errors)} errors in {output_path}"
85
+ )
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -163,9 +163,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
163
163
  elif callable(function):
164
164
  normalized_functions = compile_function_definition(function)
165
165
  elif isinstance(function, DeploymentDefinition):
166
- normalized_functions = compile_workflow_deployment_function_definition(
167
- function.model_dump(), display_context.client
168
- )
166
+ normalized_functions = compile_workflow_deployment_function_definition(function, display_context.client)
169
167
  else:
170
168
  raise ValueError(f"Unsupported function type: {type(function)}")
171
169
  return {
@@ -0,0 +1,78 @@
1
+ from vellum.workflows import BaseWorkflow
2
+ from vellum.workflows.nodes import BaseNode
3
+ from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
4
+ from vellum.workflows.state.base import BaseState
5
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
6
+
7
+
8
+ def test_final_output_node_display__serialize_with_valid_types():
9
+ # GIVEN a node that outputs a str
10
+ class StringNode(BaseNode):
11
+ class Outputs:
12
+ result: str
13
+
14
+ # AND a FinalOutputNode with matching type to that node
15
+ class CorrectOutput(FinalOutputNode[BaseState, str]):
16
+ class Outputs(FinalOutputNode.Outputs):
17
+ value = StringNode.Outputs.result
18
+
19
+ # AND a workflow referencing the node
20
+ class MyWorkflow(BaseWorkflow):
21
+ graph = StringNode >> CorrectOutput
22
+
23
+ class Outputs(BaseWorkflow.Outputs):
24
+ final_result = CorrectOutput.Outputs.value
25
+
26
+ # WHEN we serialize the workflow
27
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
28
+
29
+ # THEN serialization should succeed without raising validation errors
30
+ serialized_workflow: dict = workflow_display.serialize()
31
+
32
+ # AND the node should be properly serialized
33
+ assert "workflow_raw_data" in serialized_workflow
34
+ assert "nodes" in serialized_workflow["workflow_raw_data"]
35
+
36
+ # Find the terminal node in the serialized output
37
+ terminal_node = next(
38
+ node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] == "TERMINAL"
39
+ )
40
+ assert terminal_node is not None
41
+ assert terminal_node["id"] == str(CorrectOutput.__id__)
42
+
43
+
44
+ def test_final_output_node_display__serialize_with_invalid_types_should_raise_error():
45
+ # GIVEN a node that outputs a str
46
+ class StringNode(BaseNode):
47
+ class Outputs:
48
+ result: str
49
+
50
+ # AND a FinalOutputNode with mismatched types (expects list but gets str)
51
+ class BadOutput(FinalOutputNode[BaseState, list]):
52
+ """Output with type mismatch."""
53
+
54
+ class Outputs(FinalOutputNode.Outputs):
55
+ value = StringNode.Outputs.result # str type, conflicts with list
56
+
57
+ # AND a workflow referencing the node
58
+ class MyWorkflow(BaseWorkflow):
59
+ graph = StringNode >> BadOutput
60
+
61
+ class Outputs(BaseWorkflow.Outputs):
62
+ final_result = BadOutput.Outputs.value
63
+
64
+ # WHEN we attempt to serialize the workflow
65
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
66
+
67
+ # THEN serialization should complete without raising an exception
68
+ serialized_workflow = workflow_display.serialize()
69
+
70
+ # AND the error should be captured in workflow_display.errors
71
+ errors = list(workflow_display.display_context.errors)
72
+ assert len(errors) == 1
73
+ assert "Output type mismatch" in str(errors[0])
74
+ assert "list" in str(errors[0])
75
+ assert "str" in str(errors[0])
76
+
77
+ # AND the serialized workflow should still be created
78
+ assert "workflow_raw_data" in serialized_workflow
@@ -437,6 +437,11 @@ def test_serialize_workflow():
437
437
  "name": "max_prompt_iterations",
438
438
  "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 5.0}},
439
439
  },
440
+ {
441
+ "id": "f92dc3ec-a19a-4491-a98a-2b2df322e2e3",
442
+ "name": "settings",
443
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
444
+ },
440
445
  ],
441
446
  "outputs": [
442
447
  {"id": "e62bc785-a914-4066-b79e-8c89a5d0ec6c", "name": "text", "type": "STRING", "value": None},