vellum-ai 1.7.4__py3-none-any.whl → 1.7.6__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 (48) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/core/client_wrapper.py +2 -2
  3. vellum/client/reference.md +95 -0
  4. vellum/client/resources/workflow_deployments/client.py +111 -0
  5. vellum/client/resources/workflow_deployments/raw_client.py +121 -0
  6. vellum/client/types/__init__.py +2 -0
  7. vellum/client/types/paginated_workflow_deployment_release_list.py +30 -0
  8. vellum/client/types/vellum_error_code_enum.py +2 -0
  9. vellum/client/types/vellum_sdk_error_code_enum.py +2 -0
  10. vellum/client/types/workflow_execution_event_error_code.py +2 -0
  11. vellum/types/paginated_workflow_deployment_release_list.py +3 -0
  12. vellum/workflows/edges/__init__.py +2 -0
  13. vellum/workflows/edges/trigger_edge.py +67 -0
  14. vellum/workflows/events/tests/test_event.py +40 -0
  15. vellum/workflows/events/workflow.py +15 -3
  16. vellum/workflows/graph/graph.py +93 -0
  17. vellum/workflows/graph/tests/test_graph.py +167 -0
  18. vellum/workflows/nodes/bases/base.py +28 -9
  19. vellum/workflows/nodes/displayable/search_node/node.py +2 -1
  20. vellum/workflows/nodes/displayable/search_node/tests/test_node.py +14 -0
  21. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +7 -1
  22. vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py +1 -1
  23. vellum/workflows/nodes/displayable/tool_calling_node/node.py +1 -1
  24. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +54 -0
  25. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +27 -25
  26. vellum/workflows/ports/port.py +11 -0
  27. vellum/workflows/runner/runner.py +30 -40
  28. vellum/workflows/triggers/__init__.py +5 -0
  29. vellum/workflows/triggers/base.py +125 -0
  30. vellum/workflows/triggers/integration.py +62 -0
  31. vellum/workflows/triggers/manual.py +37 -0
  32. vellum/workflows/triggers/tests/__init__.py +1 -0
  33. vellum/workflows/triggers/tests/test_integration.py +102 -0
  34. vellum/workflows/workflows/base.py +26 -12
  35. {vellum_ai-1.7.4.dist-info → vellum_ai-1.7.6.dist-info}/METADATA +1 -1
  36. {vellum_ai-1.7.4.dist-info → vellum_ai-1.7.6.dist-info}/RECORD +48 -38
  37. vellum_cli/push.py +1 -5
  38. vellum_cli/tests/test_push.py +86 -0
  39. vellum_ee/assets/node-definitions.json +1 -1
  40. vellum_ee/workflows/display/base.py +26 -1
  41. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +1 -1
  42. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +1 -1
  43. vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +110 -0
  44. vellum_ee/workflows/display/workflows/base_workflow_display.py +72 -10
  45. vellum_ee/workflows/tests/test_server.py +40 -1
  46. {vellum_ai-1.7.4.dist-info → vellum_ai-1.7.6.dist-info}/LICENSE +0 -0
  47. {vellum_ai-1.7.4.dist-info → vellum_ai-1.7.6.dist-info}/WHEEL +0 -0
  48. {vellum_ai-1.7.4.dist-info → vellum_ai-1.7.6.dist-info}/entry_points.txt +0 -0
@@ -37,6 +37,7 @@ from vellum_ee.workflows.display.base import (
37
37
  WorkflowInputsDisplay,
38
38
  WorkflowMetaDisplay,
39
39
  WorkflowOutputDisplay,
40
+ get_trigger_type_mapping,
40
41
  )
41
42
  from vellum_ee.workflows.display.editor.types import NodeDisplayData, NodeDisplayPosition
42
43
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
@@ -408,22 +409,83 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
408
409
  except Exception as e:
409
410
  self.display_context.add_error(e)
410
411
 
411
- return {
412
- "workflow_raw_data": {
413
- "nodes": cast(JsonArray, nodes_dict_list),
414
- "edges": edges,
415
- "display_data": self.display_context.workflow_display.display_data.dict(),
416
- "definition": {
417
- "name": self._workflow.__name__,
418
- "module": cast(JsonArray, self._workflow.__module__.split(".")),
419
- },
420
- "output_values": output_values,
412
+ # Serialize workflow-level trigger if present
413
+ triggers: Optional[JsonArray] = self._serialize_workflow_trigger()
414
+
415
+ workflow_raw_data: JsonObject = {
416
+ "nodes": cast(JsonArray, nodes_dict_list),
417
+ "edges": edges,
418
+ "display_data": self.display_context.workflow_display.display_data.dict(),
419
+ "definition": {
420
+ "name": self._workflow.__name__,
421
+ "module": cast(JsonArray, self._workflow.__module__.split(".")),
421
422
  },
423
+ "output_values": output_values,
424
+ }
425
+
426
+ result: JsonObject = {
427
+ "workflow_raw_data": workflow_raw_data,
422
428
  "input_variables": input_variables,
423
429
  "state_variables": state_variables,
424
430
  "output_variables": output_variables,
425
431
  }
426
432
 
433
+ if triggers is not None:
434
+ result["triggers"] = triggers
435
+
436
+ return result
437
+
438
+ def _serialize_workflow_trigger(self) -> Optional[JsonArray]:
439
+ """
440
+ Serialize workflow-level trigger information.
441
+
442
+ Returns:
443
+ JsonArray with trigger data if a trigger is present, None otherwise.
444
+ Each trigger in the array has: id (UUID), type (str), attributes (list)
445
+ """
446
+ # Get all trigger edges from the workflow's subgraphs
447
+ trigger_edges = []
448
+ for subgraph in self._workflow.get_subgraphs():
449
+ trigger_edges.extend(list(subgraph.trigger_edges))
450
+
451
+ if not trigger_edges:
452
+ # No workflow-level trigger defined
453
+ return None
454
+
455
+ # Get the trigger class from the first edge
456
+ trigger_class = trigger_edges[0].trigger_class
457
+
458
+ # Validate that all trigger edges use the same trigger type
459
+ trigger_type_mapping = get_trigger_type_mapping()
460
+ for edge in trigger_edges:
461
+ if edge.trigger_class != trigger_class:
462
+ raise ValueError(
463
+ f"Mixed trigger types not supported. Found {trigger_class.__name__} and "
464
+ f"{edge.trigger_class.__name__} in the same workflow."
465
+ )
466
+
467
+ # Get the trigger type from the mapping
468
+ trigger_type = trigger_type_mapping.get(trigger_class)
469
+ if trigger_type is None:
470
+ raise ValueError(
471
+ f"Unknown trigger type: {trigger_class.__name__}. "
472
+ f"Please add it to the trigger type mapping in get_trigger_type_mapping()."
473
+ )
474
+
475
+ # Return as a list with a single trigger object matching Django schema
476
+ trigger_id = uuid4_from_hash(f"{trigger_class.__module__} | {trigger_class.__qualname__}")
477
+
478
+ return cast(
479
+ JsonArray,
480
+ [
481
+ {
482
+ "id": str(trigger_id),
483
+ "type": trigger_type.value,
484
+ "attributes": [],
485
+ }
486
+ ],
487
+ )
488
+
427
489
  def _serialize_edge_display_data(self, edge_display: EdgeDisplay) -> Optional[JsonObject]:
428
490
  """Serialize edge display data, returning None if no display data is present."""
429
491
  if edge_display.z_index is not None:
@@ -536,10 +536,49 @@ class BrokenNode(BaseNode) # Missing colon
536
536
 
537
537
  # AND the error message should be user-friendly
538
538
  error_message = str(exc_info.value)
539
- assert "Failed to load workflow module:" in error_message
539
+ assert "Syntax Error raised while loading Workflow:" in error_message
540
540
  assert "invalid syntax" in error_message or "expected ':'" in error_message
541
541
 
542
542
 
543
+ def test_load_from_module__name_error_in_node_file():
544
+ """
545
+ Tests that a NameError in a node file raises WorkflowInitializationException with user-facing message.
546
+ """
547
+ # GIVEN a workflow module with a node file containing a NameError (undefined class reference)
548
+ files = {
549
+ "__init__.py": "",
550
+ "workflow.py": """\
551
+ from vellum.workflows import BaseWorkflow
552
+ from .nodes.broken_node import BrokenNode
553
+
554
+ class Workflow(BaseWorkflow):
555
+ graph = BrokenNode
556
+ """,
557
+ "nodes/__init__.py": "",
558
+ "nodes/broken_node.py": """\
559
+ from vellum.workflows.nodes import BaseNode
560
+
561
+ class BrokenNode(BaseNode):
562
+ some_attribute = UndefinedClass()
563
+ """,
564
+ }
565
+
566
+ namespace = str(uuid4())
567
+
568
+ # AND the virtual file loader is registered
569
+ sys.meta_path.append(VirtualFileFinder(files, namespace))
570
+
571
+ # WHEN we attempt to load the workflow
572
+ # THEN it should raise WorkflowInitializationException
573
+ with pytest.raises(WorkflowInitializationException) as exc_info:
574
+ BaseWorkflow.load_from_module(namespace)
575
+
576
+ # AND the error message should be user-friendly
577
+ error_message = str(exc_info.value)
578
+ assert "Invalid variable reference:" in error_message
579
+ assert "UndefinedClass" in error_message or "not defined" in error_message
580
+
581
+
543
582
  def test_serialize_module__tool_calling_node_with_single_tool():
544
583
  """Test that serialize_module works with a tool calling node that has a single tool."""
545
584