vellum-ai 1.11.2__py3-none-any.whl → 1.13.5__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.
Potentially problematic release.
This version of vellum-ai might be problematic. Click here for more details.
- vellum/__init__.py +18 -0
- vellum/client/README.md +1 -1
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/core/force_multipart.py +4 -2
- vellum/client/core/http_response.py +1 -1
- vellum/client/core/pydantic_utilities.py +7 -4
- vellum/client/errors/too_many_requests_error.py +1 -2
- vellum/client/reference.md +677 -76
- vellum/client/resources/container_images/client.py +299 -0
- vellum/client/resources/container_images/raw_client.py +286 -0
- vellum/client/resources/documents/client.py +20 -10
- vellum/client/resources/documents/raw_client.py +20 -10
- vellum/client/resources/events/raw_client.py +4 -4
- vellum/client/resources/integration_auth_configs/client.py +2 -0
- vellum/client/resources/integration_auth_configs/raw_client.py +2 -0
- vellum/client/resources/integration_providers/client.py +28 -2
- vellum/client/resources/integration_providers/raw_client.py +24 -0
- vellum/client/resources/integrations/client.py +52 -4
- vellum/client/resources/integrations/raw_client.py +61 -0
- vellum/client/resources/workflow_deployments/client.py +156 -0
- vellum/client/resources/workflow_deployments/raw_client.py +334 -0
- vellum/client/resources/workflows/client.py +212 -8
- vellum/client/resources/workflows/raw_client.py +343 -6
- vellum/client/types/__init__.py +18 -0
- vellum/client/types/api_actor_type_enum.py +1 -1
- vellum/client/types/check_workflow_execution_status_error.py +21 -0
- vellum/client/types/check_workflow_execution_status_response.py +29 -0
- vellum/client/types/code_execution_package_request.py +21 -0
- vellum/client/types/composio_execute_tool_request.py +5 -0
- vellum/client/types/composio_tool_definition.py +1 -0
- vellum/client/types/container_image_build_config.py +1 -0
- vellum/client/types/container_image_container_image_tag.py +1 -0
- vellum/client/types/dataset_row_push_request.py +3 -0
- vellum/client/types/document_document_to_document_index.py +1 -0
- vellum/client/types/integration_name.py +24 -0
- vellum/client/types/node_execution_fulfilled_body.py +1 -0
- vellum/client/types/node_execution_log_body.py +24 -0
- vellum/client/types/node_execution_log_event.py +47 -0
- vellum/client/types/prompt_deployment_release_prompt_deployment.py +1 -0
- vellum/client/types/runner_config_request.py +24 -0
- vellum/client/types/severity_enum.py +5 -0
- vellum/client/types/slim_composio_tool_definition.py +1 -0
- vellum/client/types/slim_document_document_to_document_index.py +2 -0
- vellum/client/types/type_checker_enum.py +5 -0
- vellum/client/types/vellum_audio.py +5 -1
- vellum/client/types/vellum_audio_request.py +5 -1
- vellum/client/types/vellum_document.py +5 -1
- vellum/client/types/vellum_document_request.py +5 -1
- vellum/client/types/vellum_image.py +5 -1
- vellum/client/types/vellum_image_request.py +5 -1
- vellum/client/types/vellum_node_execution_event.py +2 -0
- vellum/client/types/vellum_variable.py +5 -0
- vellum/client/types/vellum_variable_extensions.py +1 -0
- vellum/client/types/vellum_variable_type.py +1 -0
- vellum/client/types/vellum_video.py +5 -1
- vellum/client/types/vellum_video_request.py +5 -1
- vellum/client/types/workflow_deployment_release_workflow_deployment.py +1 -0
- vellum/client/types/workflow_event.py +2 -0
- vellum/client/types/workflow_execution_fulfilled_body.py +1 -0
- vellum/client/types/workflow_result_event_output_data_array.py +1 -1
- vellum/client/types/workflow_result_event_output_data_chat_history.py +1 -1
- vellum/client/types/workflow_result_event_output_data_error.py +1 -1
- vellum/client/types/workflow_result_event_output_data_function_call.py +1 -1
- vellum/client/types/workflow_result_event_output_data_json.py +1 -1
- vellum/client/types/workflow_result_event_output_data_number.py +1 -1
- vellum/client/types/workflow_result_event_output_data_search_results.py +1 -1
- vellum/client/types/workflow_result_event_output_data_string.py +1 -1
- vellum/client/types/workflow_sandbox_execute_node_response.py +8 -0
- vellum/plugins/vellum_mypy.py +37 -2
- vellum/types/check_workflow_execution_status_error.py +3 -0
- vellum/types/check_workflow_execution_status_response.py +3 -0
- vellum/types/code_execution_package_request.py +3 -0
- vellum/types/node_execution_log_body.py +3 -0
- vellum/types/node_execution_log_event.py +3 -0
- vellum/types/runner_config_request.py +3 -0
- vellum/types/severity_enum.py +3 -0
- vellum/types/type_checker_enum.py +3 -0
- vellum/types/workflow_sandbox_execute_node_response.py +3 -0
- vellum/utils/files/mixin.py +26 -0
- vellum/utils/files/tests/test_mixin.py +62 -0
- vellum/utils/tests/test_vellum_client.py +95 -0
- vellum/utils/uuid.py +19 -2
- vellum/utils/vellum_client.py +10 -3
- vellum/workflows/__init__.py +7 -1
- vellum/workflows/descriptors/base.py +86 -0
- vellum/workflows/descriptors/tests/test_utils.py +9 -0
- vellum/workflows/errors/tests/__init__.py +0 -0
- vellum/workflows/errors/tests/test_types.py +52 -0
- vellum/workflows/errors/types.py +1 -0
- vellum/workflows/events/node.py +24 -0
- vellum/workflows/events/tests/test_event.py +123 -0
- vellum/workflows/events/types.py +2 -1
- vellum/workflows/events/workflow.py +28 -2
- vellum/workflows/expressions/add.py +3 -0
- vellum/workflows/expressions/tests/test_add.py +24 -0
- vellum/workflows/graph/graph.py +26 -5
- vellum/workflows/graph/tests/test_graph.py +228 -1
- vellum/workflows/inputs/base.py +22 -6
- vellum/workflows/inputs/dataset_row.py +121 -16
- vellum/workflows/inputs/tests/test_inputs.py +3 -3
- vellum/workflows/integrations/tests/test_vellum_integration_service.py +84 -0
- vellum/workflows/integrations/vellum_integration_service.py +12 -1
- vellum/workflows/loaders/base.py +2 -0
- vellum/workflows/nodes/bases/base.py +37 -16
- vellum/workflows/nodes/bases/tests/test_base_node.py +104 -1
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +1 -1
- vellum/workflows/nodes/core/map_node/node.py +7 -5
- vellum/workflows/nodes/core/map_node/tests/test_node.py +33 -0
- vellum/workflows/nodes/core/retry_node/node.py +1 -0
- vellum/workflows/nodes/core/try_node/node.py +1 -0
- vellum/workflows/nodes/displayable/api_node/node.py +3 -2
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +38 -0
- vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +18 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +109 -2
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +13 -2
- vellum/workflows/nodes/displayable/code_execution_node/node.py +9 -15
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +65 -24
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -0
- vellum/workflows/nodes/displayable/final_output_node/node.py +24 -69
- vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +53 -3
- vellum/workflows/nodes/displayable/note_node/node.py +4 -1
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +16 -5
- vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +47 -0
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +74 -34
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +204 -8
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +92 -71
- vellum/workflows/nodes/mocks.py +47 -213
- vellum/workflows/nodes/tests/test_mocks.py +0 -177
- vellum/workflows/nodes/utils.py +23 -8
- vellum/workflows/outputs/base.py +36 -3
- vellum/workflows/references/environment_variable.py +1 -11
- vellum/workflows/references/lazy.py +8 -0
- vellum/workflows/references/state_value.py +24 -1
- vellum/workflows/references/tests/test_lazy.py +58 -0
- vellum/workflows/references/trigger.py +8 -3
- vellum/workflows/references/workflow_input.py +8 -0
- vellum/workflows/resolvers/resolver.py +13 -3
- vellum/workflows/resolvers/tests/test_resolver.py +31 -0
- vellum/workflows/runner/runner.py +159 -14
- vellum/workflows/runner/tests/__init__.py +0 -0
- vellum/workflows/runner/tests/test_runner.py +170 -0
- vellum/workflows/sandbox.py +7 -8
- vellum/workflows/state/base.py +89 -30
- vellum/workflows/state/context.py +74 -3
- vellum/workflows/state/tests/test_state.py +269 -1
- vellum/workflows/tests/test_dataset_row.py +8 -7
- vellum/workflows/tests/test_sandbox.py +97 -8
- vellum/workflows/triggers/__init__.py +2 -1
- vellum/workflows/triggers/base.py +160 -28
- vellum/workflows/triggers/chat_message.py +141 -0
- vellum/workflows/triggers/integration.py +12 -0
- vellum/workflows/triggers/manual.py +3 -1
- vellum/workflows/triggers/schedule.py +3 -1
- vellum/workflows/triggers/tests/test_chat_message.py +257 -0
- vellum/workflows/types/core.py +18 -0
- vellum/workflows/types/definition.py +6 -13
- vellum/workflows/types/generics.py +12 -0
- vellum/workflows/types/tests/test_utils.py +12 -0
- vellum/workflows/types/utils.py +32 -2
- vellum/workflows/types/workflow_metadata.py +124 -0
- vellum/workflows/utils/functions.py +152 -16
- vellum/workflows/utils/pydantic_schema.py +19 -1
- vellum/workflows/utils/tests/test_functions.py +123 -8
- vellum/workflows/utils/tests/test_validate.py +79 -0
- vellum/workflows/utils/tests/test_vellum_variables.py +62 -2
- vellum/workflows/utils/uuids.py +90 -0
- vellum/workflows/utils/validate.py +108 -0
- vellum/workflows/utils/vellum_variables.py +96 -16
- vellum/workflows/workflows/base.py +177 -35
- vellum/workflows/workflows/tests/test_base_workflow.py +51 -0
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/METADATA +6 -1
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/RECORD +274 -227
- vellum_cli/__init__.py +21 -0
- vellum_cli/config.py +16 -2
- vellum_cli/pull.py +2 -0
- vellum_cli/push.py +23 -10
- vellum_cli/tests/conftest.py +8 -13
- vellum_cli/tests/test_image_push.py +4 -11
- vellum_cli/tests/test_pull.py +83 -68
- vellum_cli/tests/test_push.py +251 -2
- vellum_ee/assets/node-definitions.json +225 -12
- vellum_ee/scripts/generate_node_definitions.py +15 -3
- vellum_ee/workflows/display/base.py +4 -3
- vellum_ee/workflows/display/nodes/base_node_display.py +44 -11
- vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +93 -0
- vellum_ee/workflows/display/nodes/types.py +1 -0
- vellum_ee/workflows/display/nodes/vellum/__init__.py +0 -2
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +5 -2
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +10 -2
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +17 -14
- vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/note_node.py +18 -3
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +37 -14
- vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +62 -2
- vellum_ee/workflows/display/nodes/vellum/tests/test_final_output_node.py +136 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_note_node.py +44 -7
- vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +5 -13
- vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +27 -17
- vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +145 -22
- vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +107 -2
- vellum_ee/workflows/display/nodes/vellum/utils.py +54 -12
- vellum_ee/workflows/display/tests/test_base_workflow_display.py +13 -16
- vellum_ee/workflows/display/tests/test_json_schema_validation.py +190 -0
- vellum_ee/workflows/display/tests/test_mocks.py +912 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +14 -2
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +109 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +3 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +187 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +34 -325
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +42 -393
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +13 -315
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +2 -122
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +24 -115
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +4 -93
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +7 -80
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +9 -101
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +77 -308
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +62 -324
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -82
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +4 -142
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +1 -61
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_set_state_node_serialization.py +4 -4
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +205 -134
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +34 -146
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +2 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +8 -6
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +137 -266
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_tool_wrapper_serialization.py +84 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +55 -16
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +15 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_tool_wrapper_serialization.py +71 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_vellum_integration_serialization.py +119 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +0 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_dict_reference_serialization.py +22 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_trigger_serialization.py +412 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_code_tool_node_reference_error.py +106 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +9 -41
- vellum_ee/workflows/display/tests/workflow_serialization/test_duplicate_trigger_name_validation.py +208 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_not_referenced_by_workflow_outputs.py +45 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_infinite_loop_validation.py +66 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_int_input_serialization.py +40 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_serialization.py +8 -14
- vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_validation.py +173 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_with_entrypoint_node_id.py +16 -13
- vellum_ee/workflows/display/tests/workflow_serialization/test_list_vellum_document_serialization.py +5 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +12 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_multi_trigger_same_node_serialization.py +111 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_no_triggers_no_entrypoint_validation.py +64 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_partial_workflow_meta_display_override.py +55 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_dataset_mocks_serialization.py +268 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_invalid_pdf_data_url.py +49 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_validation_errors.py +112 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_scheduled_trigger_serialization.py +25 -16
- vellum_ee/workflows/display/tests/workflow_serialization/test_terminal_node_in_unused_graphs_serialization.py +53 -0
- vellum_ee/workflows/display/utils/exceptions.py +34 -0
- vellum_ee/workflows/display/utils/expressions.py +463 -52
- vellum_ee/workflows/display/utils/metadata.py +98 -33
- vellum_ee/workflows/display/utils/tests/test_metadata.py +31 -0
- vellum_ee/workflows/display/utils/triggers.py +153 -0
- vellum_ee/workflows/display/utils/vellum.py +59 -5
- vellum_ee/workflows/display/workflows/base_workflow_display.py +656 -254
- vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +26 -0
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +77 -29
- vellum_ee/workflows/server/namespaces.py +18 -0
- vellum_ee/workflows/tests/test_display_meta.py +2 -0
- vellum_ee/workflows/tests/test_serialize_module.py +174 -7
- vellum_ee/workflows/tests/test_server.py +0 -3
- vellum_ee/workflows/display/nodes/vellum/function_node.py +0 -14
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/LICENSE +0 -0
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/WHEEL +0 -0
- {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/entry_points.txt +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
from
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, Tuple, Type, TypeVar, cast
|
|
2
4
|
|
|
3
5
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
4
6
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
|
5
7
|
from vellum.workflows.exceptions import NodeException
|
|
8
|
+
from vellum.workflows.utils.uuids import get_state_value_id
|
|
6
9
|
|
|
7
10
|
if TYPE_CHECKING:
|
|
8
11
|
from vellum.workflows.state.base import BaseState
|
|
@@ -13,6 +16,26 @@ _T = TypeVar("_T")
|
|
|
13
16
|
|
|
14
17
|
class StateValueReference(BaseDescriptor[_T]):
|
|
15
18
|
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
name: str,
|
|
23
|
+
types: Tuple[Type[_T], ...],
|
|
24
|
+
instance: Optional[_T],
|
|
25
|
+
state_class: Type["BaseState"],
|
|
26
|
+
) -> None:
|
|
27
|
+
super().__init__(name=name, types=types, instance=instance)
|
|
28
|
+
self._state_class = state_class
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def state_class(self) -> Type["BaseState"]:
|
|
32
|
+
return self._state_class
|
|
33
|
+
|
|
34
|
+
@cached_property
|
|
35
|
+
def id(self) -> UUID:
|
|
36
|
+
"""Generate deterministic UUID from state class and state value name."""
|
|
37
|
+
return get_state_value_id(self._state_class, self.name)
|
|
38
|
+
|
|
16
39
|
def resolve(self, state: "BaseState") -> _T:
|
|
17
40
|
if hasattr(state, self._name):
|
|
18
41
|
return cast(_T, getattr(state, self._name))
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
+
from vellum.workflows import BaseWorkflow
|
|
3
4
|
from vellum.workflows.nodes import BaseNode
|
|
4
5
|
from vellum.workflows.references.lazy import LazyReference
|
|
5
6
|
|
|
6
7
|
|
|
8
|
+
class ResponseNode(BaseNode):
|
|
9
|
+
class Outputs(BaseNode.Outputs):
|
|
10
|
+
response: str = "Hello from node!"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestWorkflowWithOutput(BaseWorkflow):
|
|
14
|
+
graph = ResponseNode
|
|
15
|
+
|
|
16
|
+
class Outputs(BaseWorkflow.Outputs):
|
|
17
|
+
final_response = ResponseNode.Outputs.response
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestWorkflowWithLiteralOutput(BaseWorkflow):
|
|
21
|
+
graph = ResponseNode
|
|
22
|
+
|
|
23
|
+
class Outputs(BaseWorkflow.Outputs):
|
|
24
|
+
literal_value = "Hello literal!"
|
|
25
|
+
|
|
26
|
+
|
|
7
27
|
@pytest.fixture
|
|
8
28
|
def mock_inspect_getsource(mocker):
|
|
9
29
|
return mocker.patch("inspect.getsource")
|
|
@@ -28,3 +48,41 @@ def test_lazy_reference__inspect_getsource_fails(mock_inspect_getsource, mock_lo
|
|
|
28
48
|
|
|
29
49
|
# AND sentry is notified
|
|
30
50
|
assert mock_logger.exception.call_count == 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_lazy_reference__string_resolves_workflow_output():
|
|
54
|
+
"""Tests that string-based LazyReference can resolve workflow output references."""
|
|
55
|
+
|
|
56
|
+
# GIVEN a workflow with an output that references a node output (defined at module level)
|
|
57
|
+
# WHEN we run the workflow
|
|
58
|
+
terminal_event = TestWorkflowWithOutput().run()
|
|
59
|
+
|
|
60
|
+
# THEN the workflow completes successfully
|
|
61
|
+
assert terminal_event.name == "workflow.execution.fulfilled"
|
|
62
|
+
assert terminal_event.final_state is not None
|
|
63
|
+
|
|
64
|
+
# AND we can resolve a string-based LazyReference to the workflow output
|
|
65
|
+
lazy_ref = LazyReference[str]("TestWorkflowWithOutput.Outputs.final_response")
|
|
66
|
+
resolved_value = lazy_ref.resolve(terminal_event.final_state)
|
|
67
|
+
|
|
68
|
+
# THEN the resolved value matches the node output
|
|
69
|
+
assert resolved_value == "Hello from node!"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_lazy_reference__string_resolves_literal_workflow_output():
|
|
73
|
+
"""Tests that string-based LazyReference can resolve literal workflow output values."""
|
|
74
|
+
|
|
75
|
+
# GIVEN a workflow with a literal output value (defined at module level)
|
|
76
|
+
# WHEN we run the workflow
|
|
77
|
+
terminal_event = TestWorkflowWithLiteralOutput().run()
|
|
78
|
+
|
|
79
|
+
# THEN the workflow completes successfully
|
|
80
|
+
assert terminal_event.name == "workflow.execution.fulfilled"
|
|
81
|
+
assert terminal_event.final_state is not None
|
|
82
|
+
|
|
83
|
+
# AND we can resolve a string-based LazyReference to the literal workflow output
|
|
84
|
+
lazy_ref = LazyReference[str]("TestWorkflowWithLiteralOutput.Outputs.literal_value")
|
|
85
|
+
resolved_value = lazy_ref.resolve(terminal_event.final_state)
|
|
86
|
+
|
|
87
|
+
# THEN the resolved value matches the literal output
|
|
88
|
+
assert resolved_value == "Hello literal!"
|
|
@@ -11,7 +11,6 @@ from pydantic_core import core_schema
|
|
|
11
11
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
12
12
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
|
13
13
|
from vellum.workflows.exceptions import NodeException
|
|
14
|
-
from vellum.workflows.utils.uuids import get_trigger_attribute_id
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
17
16
|
from vellum.workflows.state.base import BaseState
|
|
@@ -40,8 +39,14 @@ class TriggerAttributeReference(BaseDescriptor[_T], Generic[_T]):
|
|
|
40
39
|
|
|
41
40
|
@property
|
|
42
41
|
def id(self) -> UUID:
|
|
43
|
-
"""
|
|
44
|
-
|
|
42
|
+
"""
|
|
43
|
+
Get the trigger attribute ID via the trigger class, which knows how to read
|
|
44
|
+
metadata.json and fall back to hash-based IDs.
|
|
45
|
+
|
|
46
|
+
This ensures trigger attribute IDs remain stable across serialization round-trips
|
|
47
|
+
when metadata.json is present.
|
|
48
|
+
"""
|
|
49
|
+
return self._trigger_class.get_attribute_id(self.name)
|
|
45
50
|
|
|
46
51
|
def resolve(self, state: BaseState) -> _T:
|
|
47
52
|
trigger_attributes = getattr(state.meta, "trigger_attributes", {})
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
from uuid import UUID
|
|
1
3
|
from typing import TYPE_CHECKING, Generic, Optional, Tuple, Type, TypeVar, cast
|
|
2
4
|
|
|
3
5
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
4
6
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
|
5
7
|
from vellum.workflows.exceptions import NodeException
|
|
6
8
|
from vellum.workflows.types.generics import import_workflow_class
|
|
9
|
+
from vellum.workflows.utils.uuids import get_workflow_input_id
|
|
7
10
|
|
|
8
11
|
if TYPE_CHECKING:
|
|
9
12
|
from vellum.workflows.inputs.base import BaseInputs
|
|
@@ -29,6 +32,11 @@ class WorkflowInputReference(BaseDescriptor[_InputType], Generic[_InputType]):
|
|
|
29
32
|
def inputs_class(self) -> Type["BaseInputs"]:
|
|
30
33
|
return self._inputs_class
|
|
31
34
|
|
|
35
|
+
@cached_property
|
|
36
|
+
def id(self) -> UUID:
|
|
37
|
+
"""Generate deterministic UUID from inputs class and input name."""
|
|
38
|
+
return get_workflow_input_id(self._inputs_class, self.name)
|
|
39
|
+
|
|
32
40
|
def resolve(self, state: "BaseState") -> _InputType:
|
|
33
41
|
if hasattr(state.meta.workflow_inputs, self._name) and (
|
|
34
42
|
state.meta.workflow_definition == self._inputs_class.__parent_class__
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
from typing import Iterator, Optional, Type, Union
|
|
4
4
|
|
|
5
|
+
from vellum.client.core.api_error import ApiError
|
|
5
6
|
from vellum.workflows.events.workflow import WorkflowEvent
|
|
6
7
|
from vellum.workflows.nodes.utils import cast_to_output_type
|
|
7
8
|
from vellum.workflows.resolvers.base import BaseWorkflowResolver
|
|
@@ -45,9 +46,18 @@ class VellumResolver(BaseWorkflowResolver):
|
|
|
45
46
|
return None
|
|
46
47
|
|
|
47
48
|
client = self._context.vellum_client
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
try:
|
|
50
|
+
response = client.workflows.retrieve_state(
|
|
51
|
+
span_id=previous_execution_id,
|
|
52
|
+
)
|
|
53
|
+
except ApiError as e:
|
|
54
|
+
if e.status_code == 404:
|
|
55
|
+
logger.debug(
|
|
56
|
+
"No state found for previous execution %s, continuing without previous state",
|
|
57
|
+
previous_execution_id,
|
|
58
|
+
)
|
|
59
|
+
return None
|
|
60
|
+
raise
|
|
51
61
|
|
|
52
62
|
if response.state is None:
|
|
53
63
|
return None
|
|
@@ -4,6 +4,7 @@ from uuid import uuid4
|
|
|
4
4
|
from typing import List
|
|
5
5
|
|
|
6
6
|
from vellum import ChatMessage
|
|
7
|
+
from vellum.client.core.api_error import ApiError
|
|
7
8
|
from vellum.client.types.workflow_resolved_state import WorkflowResolvedState
|
|
8
9
|
from vellum.workflows import BaseWorkflow
|
|
9
10
|
from vellum.workflows.inputs.base import BaseInputs
|
|
@@ -166,3 +167,33 @@ def test_load_state_with_chat_message_list():
|
|
|
166
167
|
assert result.state.chat_history[2].text == "What can you help me with?"
|
|
167
168
|
|
|
168
169
|
mock_client.workflows.retrieve_state.assert_called_once_with(span_id=str(execution_id))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_load_state_returns_none_on_404():
|
|
173
|
+
"""Test load_state returns None when retrieve_state returns 404 (e.g., rejected execution with no state)."""
|
|
174
|
+
resolver = VellumResolver()
|
|
175
|
+
execution_id = uuid4()
|
|
176
|
+
|
|
177
|
+
class TestState(BaseState):
|
|
178
|
+
test_key: str = "test_value"
|
|
179
|
+
|
|
180
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, TestState]):
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
# GIVEN a mock client that raises ApiError with 404 status
|
|
184
|
+
mock_client = Mock()
|
|
185
|
+
mock_client.workflows.retrieve_state.side_effect = ApiError(
|
|
186
|
+
status_code=404,
|
|
187
|
+
body={"detail": "No state found for the given span_id"},
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# AND context with the test workflow class is set up
|
|
191
|
+
context = WorkflowContext(vellum_client=mock_client)
|
|
192
|
+
TestWorkflow(context=context, resolvers=[resolver])
|
|
193
|
+
|
|
194
|
+
# WHEN load_state is called with an execution ID that has no state
|
|
195
|
+
result = resolver.load_state(previous_execution_id=execution_id)
|
|
196
|
+
|
|
197
|
+
# THEN should return None instead of raising an exception
|
|
198
|
+
assert result is None
|
|
199
|
+
mock_client.workflows.retrieve_state.assert_called_once_with(span_id=str(execution_id))
|
|
@@ -25,6 +25,7 @@ from typing import (
|
|
|
25
25
|
Union,
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
+
from vellum.client.core.api_error import ApiError
|
|
28
29
|
from vellum.workflows.constants import undefined
|
|
29
30
|
from vellum.workflows.context import ExecutionContext, execution_context, get_execution_context
|
|
30
31
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
@@ -49,7 +50,14 @@ from vellum.workflows.events.node import (
|
|
|
49
50
|
NodeExecutionRejectedBody,
|
|
50
51
|
NodeExecutionStreamingBody,
|
|
51
52
|
)
|
|
52
|
-
from vellum.workflows.events.types import
|
|
53
|
+
from vellum.workflows.events.types import (
|
|
54
|
+
BaseEvent,
|
|
55
|
+
NodeParentContext,
|
|
56
|
+
ParentContext,
|
|
57
|
+
SpanLink,
|
|
58
|
+
WorkflowParentContext,
|
|
59
|
+
default_serializer,
|
|
60
|
+
)
|
|
53
61
|
from vellum.workflows.events.workflow import (
|
|
54
62
|
WorkflowEventStream,
|
|
55
63
|
WorkflowExecutionFulfilledBody,
|
|
@@ -64,6 +72,7 @@ from vellum.workflows.events.workflow import (
|
|
|
64
72
|
WorkflowExecutionStreamingBody,
|
|
65
73
|
)
|
|
66
74
|
from vellum.workflows.exceptions import NodeException, WorkflowInitializationException
|
|
75
|
+
from vellum.workflows.inputs.base import BaseInputs
|
|
67
76
|
from vellum.workflows.nodes.bases import BaseNode
|
|
68
77
|
from vellum.workflows.nodes.bases.base import NodeRunResponse
|
|
69
78
|
from vellum.workflows.nodes.mocks import MockNodeExecutionArg
|
|
@@ -114,6 +123,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
114
123
|
init_execution_context: Optional[ExecutionContext] = None,
|
|
115
124
|
trigger: Optional[BaseTrigger] = None,
|
|
116
125
|
execution_id: Optional[UUID] = None,
|
|
126
|
+
event_max_size: Optional[int] = None,
|
|
117
127
|
):
|
|
118
128
|
if state and external_inputs:
|
|
119
129
|
raise ValueError("Can only run a Workflow providing one of state or external inputs, not both")
|
|
@@ -200,23 +210,28 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
200
210
|
continue
|
|
201
211
|
|
|
202
212
|
if resolver_failed:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
code=WorkflowErrorCode.INVALID_INPUTS,
|
|
213
|
+
logger.warning(
|
|
214
|
+
f"All resolvers failed to load initial state for execution ID: {previous_execution_id}. "
|
|
215
|
+
"Falling back to default state."
|
|
207
216
|
)
|
|
217
|
+
normalized_inputs = deepcopy(inputs) if inputs else self.workflow.get_default_inputs()
|
|
218
|
+
self._initial_state = self.workflow.get_default_state(normalized_inputs, execution_id)
|
|
208
219
|
|
|
209
220
|
self._entrypoints = self.workflow.get_entrypoints()
|
|
210
221
|
elif trigger:
|
|
211
222
|
# When trigger is provided, set up default state and filter entrypoints by trigger type
|
|
212
|
-
|
|
223
|
+
default_inputs = deepcopy(inputs) if inputs else None
|
|
213
224
|
if state:
|
|
214
225
|
self._initial_state = deepcopy(state)
|
|
215
|
-
self._initial_state.meta.workflow_inputs =
|
|
226
|
+
self._initial_state.meta.workflow_inputs = default_inputs
|
|
216
227
|
self._initial_state.meta.span_id = execution_id or uuid4()
|
|
217
228
|
self._initial_state.meta.workflow_definition = self.workflow.__class__
|
|
218
229
|
else:
|
|
219
|
-
self._initial_state = self.workflow.get_default_state(
|
|
230
|
+
self._initial_state = self.workflow.get_default_state(
|
|
231
|
+
default_inputs,
|
|
232
|
+
execution_id,
|
|
233
|
+
trigger_attributes={},
|
|
234
|
+
)
|
|
220
235
|
self._should_emit_initial_state = False
|
|
221
236
|
|
|
222
237
|
# Validate and bind trigger, then filter entrypoints
|
|
@@ -263,6 +278,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
263
278
|
self._timeout = timeout
|
|
264
279
|
self._execution_context = init_execution_context or get_execution_context()
|
|
265
280
|
self._trigger = trigger
|
|
281
|
+
self._event_max_size = event_max_size
|
|
266
282
|
|
|
267
283
|
setattr(
|
|
268
284
|
self._initial_state,
|
|
@@ -271,6 +287,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
271
287
|
)
|
|
272
288
|
self.workflow.context._register_event_queue(self._workflow_event_inner_queue)
|
|
273
289
|
self.workflow.context._register_node_output_mocks(node_output_mocks or [])
|
|
290
|
+
self.workflow.context._register_event_max_size(event_max_size)
|
|
274
291
|
|
|
275
292
|
self._outputs_listening_to_state = [
|
|
276
293
|
descriptor for descriptor in self.workflow.Outputs if isinstance(descriptor.instance, StateValueReference)
|
|
@@ -294,13 +311,17 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
294
311
|
Allows subclasses: if trigger_class is a subclass of any declared trigger,
|
|
295
312
|
returns those entrypoints.
|
|
296
313
|
"""
|
|
314
|
+
seen: Set[Type[BaseNode]] = set()
|
|
297
315
|
entrypoints: List[Type[BaseNode]] = []
|
|
298
316
|
for subgraph in self.workflow.get_subgraphs():
|
|
299
317
|
for trigger in subgraph.triggers:
|
|
300
318
|
# Check if the provided trigger_class is a subclass of the declared trigger
|
|
301
319
|
# This allows runtime instances to be subclasses of what's declared in the workflow
|
|
302
320
|
if issubclass(trigger_class, trigger):
|
|
303
|
-
|
|
321
|
+
for entrypoint in subgraph.entrypoints:
|
|
322
|
+
if entrypoint not in seen:
|
|
323
|
+
seen.add(entrypoint)
|
|
324
|
+
entrypoints.append(entrypoint)
|
|
304
325
|
return entrypoints
|
|
305
326
|
|
|
306
327
|
def _validate_and_bind_trigger(self, trigger: BaseTrigger) -> None:
|
|
@@ -461,6 +482,8 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
461
482
|
return state
|
|
462
483
|
|
|
463
484
|
def _emit_event(self, event: WorkflowEvent) -> WorkflowEvent:
|
|
485
|
+
if self._event_max_size is not None:
|
|
486
|
+
event._event_max_size = self._event_max_size
|
|
464
487
|
self.workflow._store.append_event(event)
|
|
465
488
|
self._background_thread_queue.put(event)
|
|
466
489
|
return event
|
|
@@ -510,6 +533,8 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
510
533
|
was_mocked: Optional[bool] = None
|
|
511
534
|
mock_candidates = node_output_mocks_map.get(node.Outputs) or []
|
|
512
535
|
for mock_candidate in mock_candidates:
|
|
536
|
+
if mock_candidate.disabled:
|
|
537
|
+
continue
|
|
513
538
|
if mock_candidate.when_condition.resolve(node.state):
|
|
514
539
|
node_run_response = mock_candidate.then_outputs
|
|
515
540
|
was_mocked = True
|
|
@@ -565,6 +590,16 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
565
590
|
|
|
566
591
|
with execution_context(parent_context=updated_parent_context, trace_id=execution.trace_id):
|
|
567
592
|
for output in node_run_response:
|
|
593
|
+
try:
|
|
594
|
+
default_serializer(output)
|
|
595
|
+
except (TypeError, ValueError) as exc:
|
|
596
|
+
raise NodeException(
|
|
597
|
+
message=(
|
|
598
|
+
f"Node {node.__class__.__name__} produced output: "
|
|
599
|
+
f"'{output.name}' that could not be serialized to JSON: {exc}"
|
|
600
|
+
),
|
|
601
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
|
602
|
+
) from exc
|
|
568
603
|
invoked_ports = output > ports
|
|
569
604
|
if output.is_initiated:
|
|
570
605
|
yield from initiate_node_streaming_output(output)
|
|
@@ -599,6 +634,20 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
599
634
|
parent=execution.parent_context,
|
|
600
635
|
)
|
|
601
636
|
|
|
637
|
+
for descriptor, output_value in outputs:
|
|
638
|
+
if output_value is undefined:
|
|
639
|
+
continue
|
|
640
|
+
try:
|
|
641
|
+
default_serializer(output_value)
|
|
642
|
+
except (TypeError, ValueError) as exc:
|
|
643
|
+
raise NodeException(
|
|
644
|
+
message=(
|
|
645
|
+
f"Node {node.__class__.__name__} produced output '{descriptor.name}' "
|
|
646
|
+
f"that could not be serialized to JSON: {exc}"
|
|
647
|
+
),
|
|
648
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
|
649
|
+
) from exc
|
|
650
|
+
|
|
602
651
|
node.state.meta.node_execution_cache.fulfill_node_execution(node.__class__, span_id)
|
|
603
652
|
|
|
604
653
|
with execution_context(parent_context=updated_parent_context, trace_id=execution.trace_id):
|
|
@@ -610,7 +659,15 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
610
659
|
continue
|
|
611
660
|
node.state.meta.node_outputs[descriptor] = output_value
|
|
612
661
|
|
|
613
|
-
|
|
662
|
+
try:
|
|
663
|
+
invoked_ports = ports(outputs, node.state)
|
|
664
|
+
except NodeException as e:
|
|
665
|
+
raise NodeException(
|
|
666
|
+
message=e.message,
|
|
667
|
+
code=e.code,
|
|
668
|
+
raw_data={**(e.raw_data or {}), "outputs": outputs.__vellum_encode__()},
|
|
669
|
+
stacktrace=e.stacktrace,
|
|
670
|
+
) from e
|
|
614
671
|
yield NodeExecutionFulfilledEvent(
|
|
615
672
|
trace_id=execution.trace_id,
|
|
616
673
|
span_id=span_id,
|
|
@@ -628,6 +685,71 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
628
685
|
yield self._handle_run_node_exception(e, "Workflow Initialization Exception", execution, span_id, node)
|
|
629
686
|
except InvalidExpressionException as e:
|
|
630
687
|
yield self._handle_run_node_exception(e, "Invalid Expression Exception", execution, span_id, node)
|
|
688
|
+
except ApiError as e:
|
|
689
|
+
captured_stacktrace = traceback.format_exc()
|
|
690
|
+
# Handle structured 403 credential error responses with integration details
|
|
691
|
+
# The Django API returns {"message": "...", "integration": {...}} for unresolvable credentials
|
|
692
|
+
# We detect this by shape (403 + integration field present) rather than a code field
|
|
693
|
+
if e.status_code == 403 and isinstance(e.body, dict) and e.body.get("integration"):
|
|
694
|
+
error_message = e.body.get(
|
|
695
|
+
"message", "You must authenticate with this integration before you can execute this tool."
|
|
696
|
+
)
|
|
697
|
+
raw_data = {"integration": e.body["integration"]}
|
|
698
|
+
yield NodeExecutionRejectedEvent(
|
|
699
|
+
trace_id=execution.trace_id,
|
|
700
|
+
span_id=span_id,
|
|
701
|
+
body=NodeExecutionRejectedBody(
|
|
702
|
+
node_definition=node.__class__,
|
|
703
|
+
error=WorkflowError(
|
|
704
|
+
message=error_message,
|
|
705
|
+
code=WorkflowErrorCode.INTEGRATION_CREDENTIALS_UNAVAILABLE,
|
|
706
|
+
raw_data=raw_data,
|
|
707
|
+
),
|
|
708
|
+
stacktrace=captured_stacktrace,
|
|
709
|
+
),
|
|
710
|
+
parent=execution.parent_context,
|
|
711
|
+
)
|
|
712
|
+
# Handle structured 400 error responses with integration details as INVALID_INPUTS
|
|
713
|
+
elif e.status_code == 400 and isinstance(e.body, dict) and e.body.get("integration"):
|
|
714
|
+
error_message = e.body.get("message", "Invalid request to integration.")
|
|
715
|
+
raw_data = {"integration": e.body["integration"]}
|
|
716
|
+
yield NodeExecutionRejectedEvent(
|
|
717
|
+
trace_id=execution.trace_id,
|
|
718
|
+
span_id=span_id,
|
|
719
|
+
body=NodeExecutionRejectedBody(
|
|
720
|
+
node_definition=node.__class__,
|
|
721
|
+
error=WorkflowError(
|
|
722
|
+
message=error_message,
|
|
723
|
+
code=WorkflowErrorCode.INVALID_INPUTS,
|
|
724
|
+
raw_data=raw_data,
|
|
725
|
+
),
|
|
726
|
+
stacktrace=captured_stacktrace,
|
|
727
|
+
),
|
|
728
|
+
parent=execution.parent_context,
|
|
729
|
+
)
|
|
730
|
+
else:
|
|
731
|
+
# For all other ApiErrors, use the existing generic exception behavior
|
|
732
|
+
error_message = self._parse_error_message(e)
|
|
733
|
+
if error_message is None:
|
|
734
|
+
logger.exception(f"An unexpected error occurred while running node {node.__class__.__name__}")
|
|
735
|
+
error_code = WorkflowErrorCode.INTERNAL_ERROR
|
|
736
|
+
error_message = "Internal error"
|
|
737
|
+
else:
|
|
738
|
+
error_code = WorkflowErrorCode.NODE_EXECUTION
|
|
739
|
+
|
|
740
|
+
yield NodeExecutionRejectedEvent(
|
|
741
|
+
trace_id=execution.trace_id,
|
|
742
|
+
span_id=span_id,
|
|
743
|
+
body=NodeExecutionRejectedBody(
|
|
744
|
+
node_definition=node.__class__,
|
|
745
|
+
error=WorkflowError(
|
|
746
|
+
message=error_message,
|
|
747
|
+
code=error_code,
|
|
748
|
+
),
|
|
749
|
+
stacktrace=captured_stacktrace,
|
|
750
|
+
),
|
|
751
|
+
parent=execution.parent_context,
|
|
752
|
+
)
|
|
631
753
|
except Exception as e:
|
|
632
754
|
error_message = self._parse_error_message(e)
|
|
633
755
|
captured_stacktrace = traceback.format_exc()
|
|
@@ -817,7 +939,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
817
939
|
)
|
|
818
940
|
)
|
|
819
941
|
)
|
|
820
|
-
|
|
942
|
+
continue
|
|
821
943
|
|
|
822
944
|
node_output_descriptor = workflow_output_descriptor.instance
|
|
823
945
|
if not isinstance(node_output_descriptor, OutputReference):
|
|
@@ -931,14 +1053,19 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
931
1053
|
),
|
|
932
1054
|
]
|
|
933
1055
|
|
|
1056
|
+
# Get raw inputs from trigger event data if available
|
|
1057
|
+
# This ensures trigger attributes are included in the serialized inputs
|
|
1058
|
+
raw_inputs = self._trigger._event_data if self._trigger else None
|
|
1059
|
+
|
|
934
1060
|
return WorkflowExecutionInitiatedEvent(
|
|
935
1061
|
trace_id=self._execution_context.trace_id,
|
|
936
1062
|
span_id=self._initial_state.meta.span_id,
|
|
937
1063
|
body=WorkflowExecutionInitiatedBody(
|
|
938
1064
|
workflow_definition=self.workflow.__class__,
|
|
939
|
-
inputs=self._initial_state.meta.workflow_inputs,
|
|
1065
|
+
inputs=self._initial_state.meta.workflow_inputs or BaseInputs(),
|
|
940
1066
|
initial_state=deepcopy(self._initial_state) if self._should_emit_initial_state else None,
|
|
941
1067
|
trigger=self._trigger.__class__ if self._trigger else None,
|
|
1068
|
+
raw_inputs=raw_inputs,
|
|
942
1069
|
),
|
|
943
1070
|
parent=self._execution_context.parent_context,
|
|
944
1071
|
links=links,
|
|
@@ -1019,6 +1146,10 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
1019
1146
|
for edge in self.workflow.get_edges():
|
|
1020
1147
|
self._dependencies[edge.to_node].add(edge.from_port.node_class)
|
|
1021
1148
|
|
|
1149
|
+
# Call trigger initiated hook so nodes can reference trigger state
|
|
1150
|
+
if self._trigger is not None:
|
|
1151
|
+
self._trigger.__on_workflow_initiated__(self._initial_state)
|
|
1152
|
+
|
|
1022
1153
|
current_parent = WorkflowParentContext(
|
|
1023
1154
|
span_id=self._initial_state.meta.span_id,
|
|
1024
1155
|
workflow_definition=self.workflow.__class__,
|
|
@@ -1078,9 +1209,11 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
1078
1209
|
self._workflow_event_outer_queue.put(event)
|
|
1079
1210
|
|
|
1080
1211
|
with execution_context(parent_context=current_parent, trace_id=self._execution_context.trace_id):
|
|
1081
|
-
|
|
1212
|
+
new_rejection_event = self._handle_work_item_event(event)
|
|
1082
1213
|
|
|
1083
|
-
if
|
|
1214
|
+
if new_rejection_event:
|
|
1215
|
+
if rejection_event is None:
|
|
1216
|
+
rejection_event = new_rejection_event
|
|
1084
1217
|
break
|
|
1085
1218
|
except Empty:
|
|
1086
1219
|
pass
|
|
@@ -1106,6 +1239,18 @@ class WorkflowRunner(Generic[StateType]):
|
|
|
1106
1239
|
)
|
|
1107
1240
|
return
|
|
1108
1241
|
|
|
1242
|
+
# Call trigger fulfilled hook before output resolution so state changes are reflected in outputs
|
|
1243
|
+
if self._trigger is not None:
|
|
1244
|
+
self._trigger.__on_workflow_fulfilled__(final_state)
|
|
1245
|
+
|
|
1246
|
+
# Drain any events produced by the trigger hook (e.g., snapshot events from state mutations)
|
|
1247
|
+
# and forward them to the outer queue so stream consumers can observe them
|
|
1248
|
+
try:
|
|
1249
|
+
while event := self._workflow_event_inner_queue.get_nowait():
|
|
1250
|
+
self._workflow_event_outer_queue.put(event)
|
|
1251
|
+
except Empty:
|
|
1252
|
+
pass
|
|
1253
|
+
|
|
1109
1254
|
fulfilled_outputs = self.workflow.Outputs()
|
|
1110
1255
|
for descriptor, value in fulfilled_outputs:
|
|
1111
1256
|
if isinstance(value, BaseDescriptor):
|
|
File without changes
|