vellum-ai 0.14.7__py3-none-any.whl → 0.14.9__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 (44) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +2 -0
  4. vellum/client/types/document_prompt_block.py +29 -0
  5. vellum/client/types/prompt_block.py +2 -0
  6. vellum/types/document_prompt_block.py +3 -0
  7. vellum/workflows/descriptors/base.py +6 -0
  8. vellum/workflows/descriptors/tests/test_utils.py +14 -0
  9. vellum/workflows/events/tests/test_event.py +40 -0
  10. vellum/workflows/events/workflow.py +20 -1
  11. vellum/workflows/expressions/greater_than.py +15 -8
  12. vellum/workflows/expressions/greater_than_or_equal_to.py +14 -8
  13. vellum/workflows/expressions/less_than.py +14 -8
  14. vellum/workflows/expressions/less_than_or_equal_to.py +14 -8
  15. vellum/workflows/expressions/parse_json.py +30 -0
  16. vellum/workflows/expressions/tests/__init__.py +0 -0
  17. vellum/workflows/expressions/tests/test_expressions.py +310 -0
  18. vellum/workflows/expressions/tests/test_parse_json.py +31 -0
  19. vellum/workflows/nodes/bases/base.py +5 -2
  20. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +34 -2
  21. vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
  22. vellum/workflows/nodes/displayable/code_execution_node/node.py +18 -8
  23. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +53 -0
  24. vellum/workflows/runner/runner.py +33 -4
  25. vellum/workflows/state/encoder.py +2 -1
  26. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/METADATA +1 -1
  27. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/RECORD +44 -38
  28. vellum_cli/__init__.py +9 -2
  29. vellum_cli/config.py +1 -0
  30. vellum_cli/init.py +6 -2
  31. vellum_cli/pull.py +1 -0
  32. vellum_cli/tests/test_init.py +194 -76
  33. vellum_cli/tests/test_pull.py +8 -0
  34. vellum_cli/tests/test_push.py +1 -0
  35. vellum_ee/workflows/display/nodes/base_node_display.py +4 -0
  36. vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +114 -0
  37. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +118 -3
  38. vellum_ee/workflows/display/types.py +1 -14
  39. vellum_ee/workflows/display/workflows/base_workflow_display.py +48 -19
  40. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +12 -0
  41. vellum_ee/workflows/tests/test_server.py +1 -0
  42. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/LICENSE +0 -0
  43. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/WHEEL +0 -0
  44. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,3 @@
1
- import pytest
2
1
  from uuid import uuid4
3
2
 
4
3
  from deepdiff import DeepDiff
@@ -217,7 +216,6 @@ def test_serialize_node__try(serialize_node):
217
216
  )
218
217
 
219
218
 
220
- @pytest.mark.skip(reason="Not implemented")
221
219
  def test_serialize_node__stacked():
222
220
  @TryNode.wrap()
223
221
  @RetryNode.wrap(max_attempts=5)
@@ -236,4 +234,121 @@ def test_serialize_node__stacked():
236
234
  exec_config = workflow_display.serialize()
237
235
 
238
236
  # THEN the workflow display is created successfully
239
- assert exec_config is not None
237
+ assert not DeepDiff(
238
+ {
239
+ "workflow_raw_data": {
240
+ "nodes": [
241
+ {
242
+ "id": "c14c1c9b-a7a4-4d2c-84fb-c940cfb09525",
243
+ "type": "ENTRYPOINT",
244
+ "inputs": [],
245
+ "data": {
246
+ "label": "Entrypoint Node",
247
+ "source_handle_id": "51a5eb25-af14-4bee-9ced-d2aa534ea8e9",
248
+ },
249
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
250
+ "base": None,
251
+ "definition": None,
252
+ },
253
+ {
254
+ "id": "074833b0-e142-4bbc-8dec-209a35e178a3",
255
+ "label": "test_serialize_node__stacked.<locals>.InnerStackedGenericNode",
256
+ "type": "GENERIC",
257
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
258
+ "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
259
+ "definition": {
260
+ "name": "InnerStackedGenericNode",
261
+ "module": [
262
+ "vellum_ee",
263
+ "workflows",
264
+ "display",
265
+ "tests",
266
+ "workflow_serialization",
267
+ "generic_nodes",
268
+ "test_adornments_serialization",
269
+ ],
270
+ },
271
+ "trigger": {"id": "f206358d-04a5-41c9-beee-0871a074fa48", "merge_behavior": "AWAIT_ATTRIBUTES"},
272
+ "ports": [{"id": "408cd5fb-3a3e-4eb2-9889-61111bd6a129", "name": "default", "type": "DEFAULT"}],
273
+ "adornments": [
274
+ {
275
+ "id": "5be7d260-74f7-4734-b31b-a46a94539586",
276
+ "label": "RetryNode",
277
+ "base": {
278
+ "name": "RetryNode",
279
+ "module": ["vellum", "workflows", "nodes", "core", "retry_node", "node"],
280
+ },
281
+ "attributes": [
282
+ {
283
+ "id": "c91782e3-140f-4938-9c23-d2a7b85dcdd8",
284
+ "name": "retry_on_error_code",
285
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
286
+ },
287
+ {
288
+ "id": "f388e93b-8c68-4f54-8577-bbd0c9091557",
289
+ "name": "max_attempts",
290
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 5}},
291
+ },
292
+ {
293
+ "id": "8a07dc58-3fed-41d4-8ca6-31ee0bb86c61",
294
+ "name": "delay",
295
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
296
+ },
297
+ {
298
+ "id": "73a02e62-4535-4e1f-97b5-1264ca8b1d71",
299
+ "name": "retry_on_condition",
300
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
301
+ },
302
+ ],
303
+ },
304
+ {
305
+ "id": "3344083c-a32c-4a32-920b-0fb5093448fa",
306
+ "label": "TryNode",
307
+ "base": {
308
+ "name": "TryNode",
309
+ "module": ["vellum", "workflows", "nodes", "core", "try_node", "node"],
310
+ },
311
+ "attributes": [
312
+ {
313
+ "id": "ab2fbab0-e2a0-419b-b1ef-ce11ecf11e90",
314
+ "name": "on_error_code",
315
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
316
+ }
317
+ ],
318
+ },
319
+ ],
320
+ "attributes": [],
321
+ "outputs": [],
322
+ },
323
+ ],
324
+ "edges": [
325
+ {
326
+ "id": "e8bd50dd-37a0-49b0-8b7b-f1dd8eb478b9",
327
+ "source_node_id": "c14c1c9b-a7a4-4d2c-84fb-c940cfb09525",
328
+ "source_handle_id": "51a5eb25-af14-4bee-9ced-d2aa534ea8e9",
329
+ "target_node_id": "074833b0-e142-4bbc-8dec-209a35e178a3",
330
+ "target_handle_id": "f206358d-04a5-41c9-beee-0871a074fa48",
331
+ "type": "DEFAULT",
332
+ }
333
+ ],
334
+ "display_data": {"viewport": {"x": 0.0, "y": 0.0, "zoom": 1.0}},
335
+ "definition": {
336
+ "name": "StackedWorkflow",
337
+ "module": [
338
+ "vellum_ee",
339
+ "workflows",
340
+ "display",
341
+ "tests",
342
+ "workflow_serialization",
343
+ "generic_nodes",
344
+ "test_adornments_serialization",
345
+ ],
346
+ },
347
+ },
348
+ "input_variables": [],
349
+ "state_variables": [],
350
+ "output_variables": [],
351
+ },
352
+ exec_config,
353
+ ignore_order=True,
354
+ )
@@ -1,9 +1,8 @@
1
1
  from dataclasses import dataclass, field
2
- from uuid import UUID
3
2
  from typing import TYPE_CHECKING, Dict, Generic, Tuple, Type, TypeVar
4
3
 
5
- from vellum.client.core import UniversalBaseModel
6
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.events.workflow import WorkflowEventDisplayContext # noqa: F401
7
6
  from vellum.workflows.nodes import BaseNode
8
7
  from vellum.workflows.ports import Port
9
8
  from vellum.workflows.references import OutputReference, StateValueReference, WorkflowInputReference
@@ -25,18 +24,6 @@ NodeDisplayType = TypeVar("NodeDisplayType", bound="BaseNodeDisplay")
25
24
  WorkflowDisplayType = TypeVar("WorkflowDisplayType", bound="BaseWorkflowDisplay")
26
25
 
27
26
 
28
- class NodeDisplay(UniversalBaseModel):
29
- input_display: Dict[str, UUID]
30
- output_display: Dict[str, UUID]
31
- port_display: Dict[str, UUID]
32
-
33
-
34
- class WorkflowEventDisplayContext(UniversalBaseModel):
35
- node_displays: Dict[str, NodeDisplay]
36
- workflow_inputs: Dict[str, UUID]
37
- workflow_outputs: Dict[str, UUID]
38
-
39
-
40
27
  @dataclass
41
28
  class WorkflowDisplayContext(
42
29
  Generic[
@@ -9,6 +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
13
  from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
13
14
  from vellum.workflows.nodes.bases import BaseNode
14
15
  from vellum.workflows.nodes.utils import get_wrapped_node
@@ -33,12 +34,7 @@ from vellum_ee.workflows.display.base import (
33
34
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
34
35
  from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
35
36
  from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
36
- from vellum_ee.workflows.display.types import (
37
- NodeDisplay,
38
- NodeDisplayType,
39
- WorkflowDisplayContext,
40
- WorkflowEventDisplayContext,
41
- )
37
+ from vellum_ee.workflows.display.types import NodeDisplayType, WorkflowDisplayContext
42
38
 
43
39
  logger = logging.getLogger(__name__)
44
40
 
@@ -217,23 +213,30 @@ class BaseWorkflowDisplay(
217
213
  # TODO: We should still serialize nodes that are in the workflow's directory but aren't used in the graph.
218
214
  # https://app.shortcut.com/vellum/story/5394
219
215
  for node in self._workflow.get_nodes():
220
- node_display = self._get_node_display(node)
216
+ extracted_node_displays = self._extract_node_displays(node)
217
+
218
+ for extracted_node, extracted_node_display in extracted_node_displays.items():
219
+ if extracted_node not in node_displays:
220
+ node_displays[extracted_node] = extracted_node_display
221
+
222
+ if extracted_node not in global_node_displays:
223
+ global_node_displays[extracted_node] = extracted_node_display
221
224
 
222
- if node not in node_displays:
223
- node_displays[node] = node_display
225
+ self._enrich_global_node_output_displays(node, extracted_node_displays[node], global_node_output_displays)
226
+ self._enrich_node_port_displays(node, extracted_node_displays[node], port_displays)
224
227
 
225
- if node not in global_node_displays:
226
- global_node_displays[node] = node_display
228
+ for node in self._workflow.get_unused_nodes():
229
+ extracted_node_displays = self._extract_node_displays(node)
227
230
 
228
- # Nodes wrapped in a decorator need to be in our node display dictionary for later retrieval
229
- inner_node = get_wrapped_node(node)
230
- if inner_node:
231
- inner_node_display = self._get_node_display(inner_node)
232
- node_displays[inner_node] = inner_node_display
233
- global_node_displays[inner_node] = inner_node_display
231
+ for extracted_node, extracted_node_display in extracted_node_displays.items():
232
+ if extracted_node not in node_displays:
233
+ node_displays[extracted_node] = extracted_node_display
234
234
 
235
- self._enrich_global_node_output_displays(node, node_display, global_node_output_displays)
236
- self._enrich_node_port_displays(node, node_display, port_displays)
235
+ if extracted_node not in global_node_displays:
236
+ global_node_displays[extracted_node] = extracted_node_display
237
+
238
+ self._enrich_global_node_output_displays(node, extracted_node_displays[node], global_node_output_displays)
239
+ self._enrich_node_port_displays(node, extracted_node_displays[node], port_displays)
237
240
 
238
241
  workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = {}
239
242
  # If we're dealing with a nested workflow, then it should have access to the inputs of its parents.
@@ -280,6 +283,15 @@ class BaseWorkflowDisplay(
280
283
  edge, node_displays, port_displays, overrides=edge_display_overrides
281
284
  )
282
285
 
286
+ for edge in self._workflow.get_unused_edges():
287
+ if edge in edge_displays:
288
+ continue
289
+
290
+ edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
291
+ edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
292
+ edge, node_displays, port_displays, overrides=edge_display_overrides
293
+ )
294
+
283
295
  workflow_output_displays: Dict[BaseDescriptor, WorkflowOutputDisplay] = {}
284
296
  for workflow_output in self._workflow.Outputs:
285
297
  if workflow_output in workflow_output_displays:
@@ -409,3 +421,20 @@ class BaseWorkflowDisplay(
409
421
  node_displays=temp_node_displays,
410
422
  )
411
423
  return display_meta
424
+
425
+ def _extract_node_displays(self, node: Type[BaseNode]) -> Dict[Type[BaseNode], NodeDisplayType]:
426
+ node_display = self._get_node_display(node)
427
+ additional_node_displays: Dict[Type[BaseNode], NodeDisplayType] = {
428
+ node: node_display,
429
+ }
430
+
431
+ # Nodes wrapped in a decorator need to be in our node display dictionary for later retrieval
432
+ inner_node = get_wrapped_node(node)
433
+ if inner_node:
434
+ inner_node_displays = self._extract_node_displays(inner_node)
435
+
436
+ for node, display in inner_node_displays.items():
437
+ if node not in additional_node_displays:
438
+ additional_node_displays[node] = display
439
+
440
+ return additional_node_displays
@@ -126,6 +126,18 @@ class VellumWorkflowDisplay(
126
126
 
127
127
  nodes.append(serialized_node)
128
128
 
129
+ # Add all unused nodes in the workflow
130
+ for node in self._workflow.get_unused_nodes():
131
+ node_display = self.display_context.node_displays[node]
132
+
133
+ try:
134
+ serialized_node = node_display.serialize(self.display_context)
135
+ except NotImplementedError as e:
136
+ self.add_error(e)
137
+ continue
138
+
139
+ nodes.append(serialized_node)
140
+
129
141
  synthetic_output_edges: JsonArray = []
130
142
  output_variables: JsonArray = []
131
143
  final_output_nodes = [
@@ -2,6 +2,7 @@ from vellum.client.core.pydantic_utilities import UniversalBaseModel
2
2
 
3
3
 
4
4
  def test_load_workflow_event_display_context():
5
+ # DEPRECATED: Use `vellum.workflows.events.workflow.WorkflowEventDisplayContext` instead. Will be removed in 0.15.0
5
6
  from vellum_ee.workflows.display.types import WorkflowEventDisplayContext
6
7
 
7
8
  # We are actually just ensuring there are no circular dependencies when