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,14 +1,15 @@
|
|
|
1
|
-
from typing import Any, Dict, Generic, Tuple, Type, TypeVar, get_args
|
|
1
|
+
from typing import Any, Dict, Generic, Tuple, Type, TypeVar, get_args
|
|
2
2
|
|
|
3
3
|
from vellum.workflows.constants import undefined
|
|
4
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
4
5
|
from vellum.workflows.nodes.bases import BaseNode
|
|
5
6
|
from vellum.workflows.nodes.bases.base import BaseNodeMeta
|
|
6
7
|
from vellum.workflows.nodes.utils import cast_to_output_type
|
|
7
8
|
from vellum.workflows.ports import NodePorts
|
|
8
|
-
from vellum.workflows.references.output import OutputReference
|
|
9
9
|
from vellum.workflows.types import MergeBehavior
|
|
10
10
|
from vellum.workflows.types.generics import StateType
|
|
11
11
|
from vellum.workflows.types.utils import get_original_base
|
|
12
|
+
from vellum.workflows.utils.validate import validate_target_type
|
|
12
13
|
|
|
13
14
|
_OutputType = TypeVar("_OutputType")
|
|
14
15
|
|
|
@@ -40,73 +41,6 @@ class _FinalOutputNodeMeta(BaseNodeMeta):
|
|
|
40
41
|
else:
|
|
41
42
|
return all_args[1]
|
|
42
43
|
|
|
43
|
-
def __validate__(cls) -> None:
|
|
44
|
-
cls._validate_output_type_consistency(cls)
|
|
45
|
-
|
|
46
|
-
@classmethod
|
|
47
|
-
def _validate_output_type_consistency(mcs, cls: Type) -> None:
|
|
48
|
-
"""
|
|
49
|
-
Validates that the declared output type of FinalOutputNode matches
|
|
50
|
-
the type of the descriptor assigned to the 'value' attribute in its Outputs class.
|
|
51
|
-
|
|
52
|
-
Raises ValueError if there's a type mismatch.
|
|
53
|
-
"""
|
|
54
|
-
if not hasattr(cls, "Outputs"):
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
outputs_class = cls.Outputs
|
|
58
|
-
if not hasattr(outputs_class, "value"):
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
declared_output_type = cls.get_output_type()
|
|
62
|
-
|
|
63
|
-
if declared_output_type is Any:
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
value_descriptor = None
|
|
67
|
-
|
|
68
|
-
if "value" in outputs_class.__dict__:
|
|
69
|
-
value_descriptor = outputs_class.__dict__["value"]
|
|
70
|
-
else:
|
|
71
|
-
value_descriptor = getattr(outputs_class, "value")
|
|
72
|
-
|
|
73
|
-
if isinstance(value_descriptor, OutputReference):
|
|
74
|
-
descriptor_types = value_descriptor.types
|
|
75
|
-
|
|
76
|
-
type_mismatch = True
|
|
77
|
-
for descriptor_type in descriptor_types:
|
|
78
|
-
if descriptor_type == declared_output_type:
|
|
79
|
-
type_mismatch = False
|
|
80
|
-
break
|
|
81
|
-
if (
|
|
82
|
-
get_origin(descriptor_type) == declared_output_type
|
|
83
|
-
or get_origin(declared_output_type) == descriptor_type
|
|
84
|
-
):
|
|
85
|
-
type_mismatch = False
|
|
86
|
-
break
|
|
87
|
-
try:
|
|
88
|
-
if issubclass(descriptor_type, declared_output_type) or issubclass(
|
|
89
|
-
declared_output_type, descriptor_type
|
|
90
|
-
):
|
|
91
|
-
type_mismatch = False
|
|
92
|
-
break
|
|
93
|
-
except TypeError:
|
|
94
|
-
# Handle cases where types aren't classes (e.g., Union)
|
|
95
|
-
if str(descriptor_type) == str(declared_output_type):
|
|
96
|
-
type_mismatch = False
|
|
97
|
-
break
|
|
98
|
-
|
|
99
|
-
if type_mismatch:
|
|
100
|
-
declared_type_name = getattr(declared_output_type, "__name__", str(declared_output_type))
|
|
101
|
-
descriptor_type_names = [getattr(t, "__name__", str(t)) for t in descriptor_types]
|
|
102
|
-
|
|
103
|
-
raise ValueError(
|
|
104
|
-
f"Output type mismatch in {cls.__name__}: "
|
|
105
|
-
f"FinalOutputNode is declared with output type '{declared_type_name}' "
|
|
106
|
-
f"but the 'value' descriptor has type(s) {descriptor_type_names}. "
|
|
107
|
-
f"The output descriptor type must match the declared FinalOutputNode output type."
|
|
108
|
-
)
|
|
109
|
-
|
|
110
44
|
|
|
111
45
|
class FinalOutputNode(BaseNode[StateType], Generic[StateType, _OutputType], metaclass=_FinalOutputNodeMeta):
|
|
112
46
|
"""
|
|
@@ -140,3 +74,24 @@ class FinalOutputNode(BaseNode[StateType], Generic[StateType, _OutputType], meta
|
|
|
140
74
|
)
|
|
141
75
|
|
|
142
76
|
__simulates_workflow_output__ = True
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def __validate__(cls) -> None:
|
|
80
|
+
cls._validate_output_type_consistency()
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def _validate_output_type_consistency(cls) -> None:
|
|
84
|
+
"""
|
|
85
|
+
Validates that the declared output type of FinalOutputNode matches
|
|
86
|
+
the type of the descriptor assigned to the 'value' attribute in its Outputs class.
|
|
87
|
+
|
|
88
|
+
Raises ValueError if there's a type mismatch.
|
|
89
|
+
"""
|
|
90
|
+
declared_output_type = cls.get_output_type()
|
|
91
|
+
value_descriptor = cls.Outputs.value.instance
|
|
92
|
+
|
|
93
|
+
if isinstance(value_descriptor, BaseDescriptor):
|
|
94
|
+
try:
|
|
95
|
+
validate_target_type(declared_output_type, value_descriptor.normalized_type)
|
|
96
|
+
except ValueError as e:
|
|
97
|
+
raise ValueError(f"Failed to validate output type for node '{cls.__name__}': {e}")
|
|
@@ -2,12 +2,14 @@ import pytest
|
|
|
2
2
|
from typing import Any, Dict
|
|
3
3
|
|
|
4
4
|
from vellum.workflows.exceptions import NodeException
|
|
5
|
+
from vellum.workflows.nodes.core.map_node import MapNode
|
|
5
6
|
from vellum.workflows.nodes.core.templating_node import TemplatingNode
|
|
6
7
|
from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
|
|
7
8
|
from vellum.workflows.nodes.displayable.inline_prompt_node import InlinePromptNode
|
|
8
9
|
from vellum.workflows.references.output import OutputReference
|
|
9
10
|
from vellum.workflows.state.base import BaseState
|
|
10
11
|
from vellum.workflows.types.core import Json
|
|
12
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
def test_final_output_node__mismatched_output_type_should_raise_exception_when_ran():
|
|
@@ -41,9 +43,31 @@ def test_final_output_node__mismatched_output_type_should_raise_exception():
|
|
|
41
43
|
# AND the error message should indicate the type mismatch
|
|
42
44
|
assert (
|
|
43
45
|
str(exc_info.value)
|
|
44
|
-
== "
|
|
45
|
-
"the 'value'
|
|
46
|
-
|
|
46
|
+
== "Failed to validate output type for node 'Output': Output type mismatch: declared type 'list' but "
|
|
47
|
+
"the 'value' Output has type(s) 'str'. "
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_final_output_node__mismatched_output_type_in_state_should_raise_exception():
|
|
52
|
+
# GIVEN a state with a str type
|
|
53
|
+
class State(BaseState):
|
|
54
|
+
foo: str
|
|
55
|
+
|
|
56
|
+
# AND a FinalOutputNode declared with list output type
|
|
57
|
+
class Output(FinalOutputNode[BaseState, list]):
|
|
58
|
+
class Outputs(FinalOutputNode.Outputs):
|
|
59
|
+
value = State.foo
|
|
60
|
+
|
|
61
|
+
# WHEN attempting to validate the node class
|
|
62
|
+
# THEN a ValueError should be raised during validation
|
|
63
|
+
with pytest.raises(ValueError) as exc_info:
|
|
64
|
+
Output.__validate__()
|
|
65
|
+
|
|
66
|
+
# AND the error message should indicate the type mismatch
|
|
67
|
+
assert (
|
|
68
|
+
str(exc_info.value)
|
|
69
|
+
== "Failed to validate output type for node 'Output': Output type mismatch: declared type 'list' but "
|
|
70
|
+
"the 'value' Output has type(s) 'str'. "
|
|
47
71
|
)
|
|
48
72
|
|
|
49
73
|
|
|
@@ -113,3 +137,29 @@ def test_final_output_node__any_output_type_should_accept_json():
|
|
|
113
137
|
AnyOutputNode.__validate__()
|
|
114
138
|
except ValueError as e:
|
|
115
139
|
pytest.fail(f"Validation should not raise an exception when Any accepts Json: {e}")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_final_output_node__list_str_output_type_should_pass_validation():
|
|
143
|
+
"""
|
|
144
|
+
Tests that FinalOutputNode with list[str] output type accepts a descriptor with List[str] type.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
# GIVEN value descriptor has List[str] type
|
|
148
|
+
class MySubworkflow(BaseWorkflow):
|
|
149
|
+
class Outputs(BaseWorkflow.Outputs):
|
|
150
|
+
result: str
|
|
151
|
+
|
|
152
|
+
class MyMap(MapNode):
|
|
153
|
+
subworkflow = MySubworkflow
|
|
154
|
+
|
|
155
|
+
# AND a FinalOutputNode declared with list[str] output type
|
|
156
|
+
class ListStrOutputNode(FinalOutputNode[BaseState, list[str]]):
|
|
157
|
+
class Outputs(FinalOutputNode.Outputs):
|
|
158
|
+
value = MyMap.Outputs.result
|
|
159
|
+
|
|
160
|
+
# WHEN attempting to validate the node class
|
|
161
|
+
try:
|
|
162
|
+
ListStrOutputNode.__validate__()
|
|
163
|
+
except Exception as e:
|
|
164
|
+
# THEN validation should pass without raising an exception
|
|
165
|
+
pytest.fail(f"Validation should not raise an exception for list[str]/List[str] compatibility: {e}")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Generic
|
|
1
|
+
from typing import Any, Dict, Generic, Optional
|
|
2
2
|
|
|
3
3
|
from vellum.workflows.nodes.bases import BaseNode
|
|
4
4
|
from vellum.workflows.types import MergeBehavior
|
|
@@ -10,6 +10,9 @@ class NoteNode(BaseNode[StateType], Generic[StateType]):
|
|
|
10
10
|
A no-op Node purely used to display a note in the Vellum UI.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
text: str = ""
|
|
14
|
+
style: Optional[Dict[str, Any]] = None
|
|
15
|
+
|
|
13
16
|
class Display(BaseNode.Display):
|
|
14
17
|
icon = "vellum:icon:note"
|
|
15
18
|
color = "cyan"
|
|
@@ -21,6 +21,7 @@ from vellum.workflows.constants import LATEST_RELEASE_TAG, OMIT, undefined
|
|
|
21
21
|
from vellum.workflows.context import execution_context, get_execution_context, get_parent_context
|
|
22
22
|
from vellum.workflows.errors import WorkflowErrorCode
|
|
23
23
|
from vellum.workflows.errors.types import workflow_event_error_to_workflow_error
|
|
24
|
+
from vellum.workflows.events.exception_handling import stream_initialization_exception
|
|
24
25
|
from vellum.workflows.events.types import WorkflowDeploymentParentContext, default_serializer
|
|
25
26
|
from vellum.workflows.events.workflow import is_workflow_event
|
|
26
27
|
from vellum.workflows.exceptions import NodeException, WorkflowInitializationException
|
|
@@ -191,16 +192,26 @@ class SubworkflowDeploymentNode(BaseNode[StateType], Generic[StateType]):
|
|
|
191
192
|
resolved_workflow = workflow_class(
|
|
192
193
|
context=WorkflowContext.create_from(self._context), parent_state=self.state
|
|
193
194
|
)
|
|
194
|
-
subworkflow_stream = resolved_workflow.stream(
|
|
195
|
-
inputs=self._compile_subworkflow_inputs_for_direct_invocation(resolved_workflow),
|
|
196
|
-
event_filter=all_workflow_event_filter,
|
|
197
|
-
node_output_mocks=self._context._get_all_node_output_mocks(),
|
|
198
|
-
)
|
|
199
195
|
|
|
200
196
|
try:
|
|
197
|
+
# The stream creation and first event retrieval are wrapped in try/except because
|
|
198
|
+
# WorkflowInitializationException can be raised during stream creation (e.g., when
|
|
199
|
+
# inputs are invalid) or when getting the first event
|
|
200
|
+
subworkflow_inputs = self._compile_subworkflow_inputs_for_direct_invocation(resolved_workflow)
|
|
201
|
+
subworkflow_stream = resolved_workflow.stream(
|
|
202
|
+
inputs=subworkflow_inputs,
|
|
203
|
+
event_filter=all_workflow_event_filter,
|
|
204
|
+
node_output_mocks=self._context._get_all_node_output_mocks(),
|
|
205
|
+
event_max_size=self._context.event_max_size,
|
|
206
|
+
)
|
|
201
207
|
first_event = next(subworkflow_stream)
|
|
202
208
|
self._context._emit_subworkflow_event(first_event)
|
|
203
209
|
except WorkflowInitializationException as e:
|
|
210
|
+
# Emit initiated and rejected events for the subworkflow so that
|
|
211
|
+
# the parent workflow can see the subworkflow's lifecycle events
|
|
212
|
+
for init_failure_event in stream_initialization_exception(e):
|
|
213
|
+
self._context._emit_subworkflow_event(init_failure_event)
|
|
214
|
+
|
|
204
215
|
hashed_module = e.definition.__module__
|
|
205
216
|
raise NodeException(
|
|
206
217
|
message=e.message,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pytest
|
|
1
2
|
from uuid import uuid4
|
|
2
3
|
from typing import Any, Iterator, List
|
|
3
4
|
|
|
@@ -6,8 +7,12 @@ from vellum import (
|
|
|
6
7
|
FulfilledExecutePromptEvent,
|
|
7
8
|
InitiatedExecutePromptEvent,
|
|
8
9
|
PromptOutput,
|
|
10
|
+
RejectedExecutePromptEvent,
|
|
9
11
|
StringVellumValue,
|
|
12
|
+
VellumError,
|
|
10
13
|
)
|
|
14
|
+
from vellum.workflows.errors import WorkflowErrorCode
|
|
15
|
+
from vellum.workflows.exceptions import NodeException
|
|
11
16
|
from vellum.workflows.inputs import BaseInputs
|
|
12
17
|
from vellum.workflows.nodes import PromptDeploymentNode
|
|
13
18
|
from vellum.workflows.state import BaseState
|
|
@@ -73,3 +78,45 @@ def test_text_prompt_deployment_node__basic(vellum_client):
|
|
|
73
78
|
expand_meta = call_kwargs.get("expand_meta")
|
|
74
79
|
assert expand_meta is not None
|
|
75
80
|
assert expand_meta.finish_reason is True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_prompt_deployment_node__provider_error_from_first_event(vellum_client):
|
|
84
|
+
"""
|
|
85
|
+
Tests that PromptDeploymentNode raises NodeException with the actual provider error message
|
|
86
|
+
when the first event is REJECTED, rather than a generic "Expected to receive outputs" error.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# GIVEN a PromptDeploymentNode with basic configuration
|
|
90
|
+
class MyPromptDeploymentNode(PromptDeploymentNode):
|
|
91
|
+
deployment = "my-deployment"
|
|
92
|
+
|
|
93
|
+
# AND the API returns a REJECTED event as the first event with a provider quota error
|
|
94
|
+
provider_error = VellumError(
|
|
95
|
+
code="PROVIDER_QUOTA_EXCEEDED",
|
|
96
|
+
message="Google: You exceeded your current quota, please check your plan and billing details.",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def generate_prompt_events(*args: Any, **kwargs: Any) -> Iterator[ExecutePromptEvent]:
|
|
100
|
+
execution_id = str(uuid4())
|
|
101
|
+
events: List[ExecutePromptEvent] = [
|
|
102
|
+
RejectedExecutePromptEvent(
|
|
103
|
+
execution_id=execution_id,
|
|
104
|
+
error=provider_error,
|
|
105
|
+
),
|
|
106
|
+
]
|
|
107
|
+
yield from events
|
|
108
|
+
|
|
109
|
+
vellum_client.execute_prompt_stream.side_effect = generate_prompt_events
|
|
110
|
+
|
|
111
|
+
# WHEN the node is run
|
|
112
|
+
node = MyPromptDeploymentNode()
|
|
113
|
+
|
|
114
|
+
# THEN it should raise a NodeException with the actual provider error
|
|
115
|
+
with pytest.raises(NodeException) as excinfo:
|
|
116
|
+
list(node.run())
|
|
117
|
+
|
|
118
|
+
# AND the exception should have the correct error code
|
|
119
|
+
assert excinfo.value.code == WorkflowErrorCode.PROVIDER_QUOTA_EXCEEDED
|
|
120
|
+
|
|
121
|
+
# AND the exception message should contain the actual provider error message
|
|
122
|
+
assert "Google: You exceeded your current quota" in excinfo.value.message
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
from typing import Any, ClassVar, Dict, Generic, Iterator, List, Optional, Set, Union
|
|
1
|
+
from typing import Any, ClassVar, Dict, Generic, Iterator, List, Optional, Set, Type, Union, cast
|
|
2
2
|
|
|
3
3
|
from vellum import ChatMessage, PromptBlock, PromptOutput
|
|
4
4
|
from vellum.client.types.prompt_parameters import PromptParameters
|
|
5
5
|
from vellum.client.types.prompt_settings import PromptSettings
|
|
6
6
|
from vellum.client.types.string_chat_message_content import StringChatMessageContent
|
|
7
7
|
from vellum.prompts.constants import DEFAULT_PROMPT_PARAMETERS
|
|
8
|
+
from vellum.workflows.constants import undefined
|
|
8
9
|
from vellum.workflows.context import execution_context, get_parent_context
|
|
10
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
|
9
11
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
|
12
|
+
from vellum.workflows.events.node import NodeExecutionStreamingEvent
|
|
10
13
|
from vellum.workflows.events.workflow import is_workflow_event
|
|
11
14
|
from vellum.workflows.exceptions import NodeException
|
|
15
|
+
from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
|
|
12
16
|
from vellum.workflows.graph.graph import Graph
|
|
13
17
|
from vellum.workflows.inputs.base import BaseInputs
|
|
14
18
|
from vellum.workflows.nodes.bases import BaseNode
|
|
@@ -16,20 +20,30 @@ from vellum.workflows.nodes.displayable.tool_calling_node.state import ToolCalli
|
|
|
16
20
|
from vellum.workflows.nodes.displayable.tool_calling_node.utils import (
|
|
17
21
|
create_else_node,
|
|
18
22
|
create_function_node,
|
|
19
|
-
create_mcp_tool_node,
|
|
20
23
|
create_router_node,
|
|
21
24
|
create_tool_prompt_node,
|
|
22
25
|
get_function_name,
|
|
23
26
|
)
|
|
24
27
|
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
|
28
|
+
from vellum.workflows.references.output import OutputReference
|
|
25
29
|
from vellum.workflows.state.context import WorkflowContext
|
|
26
30
|
from vellum.workflows.types.core import EntityInputsInterface
|
|
27
|
-
from vellum.workflows.types.definition import MCPServer, Tool
|
|
31
|
+
from vellum.workflows.types.definition import MCPServer, MCPToolDefinition, Tool, ToolBase
|
|
28
32
|
from vellum.workflows.types.generics import StateType
|
|
29
|
-
from vellum.workflows.utils.functions import compile_mcp_tool_definition
|
|
33
|
+
from vellum.workflows.utils.functions import compile_mcp_tool_definition
|
|
30
34
|
from vellum.workflows.workflows.event_filters import all_workflow_event_filter
|
|
31
35
|
|
|
32
36
|
|
|
37
|
+
def _contains_reference_to_output(reference: BaseDescriptor, target_reference: OutputReference) -> bool:
|
|
38
|
+
if reference == target_reference:
|
|
39
|
+
return True
|
|
40
|
+
if isinstance(reference, CoalesceExpression):
|
|
41
|
+
return _contains_reference_to_output(reference.lhs, target_reference) or _contains_reference_to_output(
|
|
42
|
+
reference.rhs, target_reference
|
|
43
|
+
)
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
|
|
33
47
|
class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
34
48
|
"""
|
|
35
49
|
A Node that dynamically invokes the provided functions to the underlying Prompt
|
|
@@ -60,10 +74,12 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
60
74
|
The outputs of the ToolCallingNode.
|
|
61
75
|
|
|
62
76
|
text: The final text response after tool calling
|
|
77
|
+
json: The result of the Prompt Execution in JSON format (if parseable)
|
|
63
78
|
chat_history: The complete chat history including tool calls
|
|
64
79
|
"""
|
|
65
80
|
|
|
66
81
|
text: str
|
|
82
|
+
json: Union[Dict[Any, Any], Type[undefined]] = undefined
|
|
67
83
|
chat_history: List[ChatMessage]
|
|
68
84
|
|
|
69
85
|
def run(self) -> Iterator[BaseOutput]:
|
|
@@ -86,6 +102,7 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
86
102
|
|
|
87
103
|
class Outputs(BaseWorkflow.Outputs):
|
|
88
104
|
text: str = self.tool_prompt_node.Outputs.text
|
|
105
|
+
json: Any = self.tool_prompt_node.Outputs.json
|
|
89
106
|
chat_history: List[ChatMessage] = ToolCallingState.chat_history
|
|
90
107
|
results: List[PromptOutput] = self.tool_prompt_node.Outputs.results
|
|
91
108
|
|
|
@@ -97,6 +114,7 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
97
114
|
subworkflow_stream = subworkflow.stream(
|
|
98
115
|
event_filter=all_workflow_event_filter,
|
|
99
116
|
node_output_mocks=self._context._get_all_node_output_mocks(),
|
|
117
|
+
event_max_size=self._context.event_max_size,
|
|
100
118
|
)
|
|
101
119
|
|
|
102
120
|
outputs: Optional[BaseOutputs] = None
|
|
@@ -139,17 +157,6 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
139
157
|
exception = NodeException.of(event.error)
|
|
140
158
|
|
|
141
159
|
if exception:
|
|
142
|
-
# In the case of the Prompt Node receiving invalid inputs or schemas, that is a bug with the
|
|
143
|
-
# internals of the Agent Node. In the future, we should distinguish further by also checking
|
|
144
|
-
# that this exception came from the Tool Calling Node's prompt node instead of anything
|
|
145
|
-
# internal to a user defined tool.
|
|
146
|
-
if exception.code == WorkflowErrorCode.INVALID_INPUTS:
|
|
147
|
-
raise NodeException(
|
|
148
|
-
message="Internal server error",
|
|
149
|
-
code=WorkflowErrorCode.INTERNAL_ERROR,
|
|
150
|
-
raw_data=exception.raw_data,
|
|
151
|
-
stacktrace=exception.stacktrace,
|
|
152
|
-
) from exception
|
|
153
160
|
raise exception
|
|
154
161
|
|
|
155
162
|
if outputs is None:
|
|
@@ -170,10 +177,20 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
170
177
|
process_parameters_method = getattr(self.__class__, "process_parameters", None)
|
|
171
178
|
process_blocks_method = getattr(self.__class__, "process_blocks", None)
|
|
172
179
|
|
|
180
|
+
# Hydrate MCP servers upfront and replace them with tool definitions
|
|
181
|
+
hydrated_functions: List[Union[ToolBase, MCPToolDefinition]] = []
|
|
182
|
+
for function in self.functions:
|
|
183
|
+
if isinstance(function, MCPServer):
|
|
184
|
+
hydrated_functions.extend(compile_mcp_tool_definition(function))
|
|
185
|
+
else:
|
|
186
|
+
# After checking, function is ToolBase (not MCPServer)
|
|
187
|
+
# Mypy doesn't narrow Union[ToolBase, MCPServer] to ToolBase, so we cast
|
|
188
|
+
hydrated_functions.append(cast(ToolBase, function))
|
|
189
|
+
|
|
173
190
|
self.tool_prompt_node = create_tool_prompt_node(
|
|
174
191
|
ml_model=self.ml_model,
|
|
175
192
|
blocks=self.blocks,
|
|
176
|
-
functions=
|
|
193
|
+
functions=hydrated_functions,
|
|
177
194
|
prompt_inputs=self.prompt_inputs,
|
|
178
195
|
parameters=self.parameters,
|
|
179
196
|
max_prompt_iterations=self.max_prompt_iterations,
|
|
@@ -184,28 +201,17 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
184
201
|
|
|
185
202
|
# Create the router node (handles routing logic only)
|
|
186
203
|
self.router_node = create_router_node(
|
|
187
|
-
functions=
|
|
204
|
+
functions=hydrated_functions,
|
|
188
205
|
tool_prompt_node=self.tool_prompt_node,
|
|
189
206
|
)
|
|
190
207
|
|
|
191
208
|
self._function_nodes = {}
|
|
192
|
-
for
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
self._function_nodes[function_name] = create_mcp_tool_node(
|
|
199
|
-
tool_def=tool_definition,
|
|
200
|
-
tool_prompt_node=self.tool_prompt_node,
|
|
201
|
-
)
|
|
202
|
-
else:
|
|
203
|
-
function_name = get_function_name(function)
|
|
204
|
-
|
|
205
|
-
self._function_nodes[function_name] = create_function_node(
|
|
206
|
-
function=function,
|
|
207
|
-
tool_prompt_node=self.tool_prompt_node,
|
|
208
|
-
)
|
|
209
|
+
for hydrated_function in hydrated_functions:
|
|
210
|
+
function_name = get_function_name(hydrated_function)
|
|
211
|
+
self._function_nodes[function_name] = create_function_node(
|
|
212
|
+
function=hydrated_function,
|
|
213
|
+
tool_prompt_node=self.tool_prompt_node,
|
|
214
|
+
)
|
|
209
215
|
|
|
210
216
|
graph: Graph = self.tool_prompt_node >> self.router_node
|
|
211
217
|
|
|
@@ -223,3 +229,37 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
|
223
229
|
graph._extend_edges(default_port_graph.edges)
|
|
224
230
|
|
|
225
231
|
self._graph = graph
|
|
232
|
+
|
|
233
|
+
def __directly_emit_workflow_output__(
|
|
234
|
+
self,
|
|
235
|
+
event: NodeExecutionStreamingEvent,
|
|
236
|
+
workflow_output_descriptor: OutputReference,
|
|
237
|
+
) -> bool:
|
|
238
|
+
"""
|
|
239
|
+
Check if this ToolCallingNode should directly emit workflow output events.
|
|
240
|
+
Similar to BasePromptNode, this allows streaming text and chat_history outputs through FinalOutputNode.
|
|
241
|
+
"""
|
|
242
|
+
# Only handle text and chat_history outputs
|
|
243
|
+
if event.output.name != "text":
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
if not isinstance(event.output.delta, str) and not event.output.is_initiated:
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
target_nodes = [e.to_node for port in self.Ports for e in port.edges if e.to_node.__simulates_workflow_output__]
|
|
250
|
+
target_node_output = next(
|
|
251
|
+
(
|
|
252
|
+
o
|
|
253
|
+
for target_node in target_nodes
|
|
254
|
+
for o in target_node.Outputs
|
|
255
|
+
if o == workflow_output_descriptor.instance
|
|
256
|
+
),
|
|
257
|
+
None,
|
|
258
|
+
)
|
|
259
|
+
if not target_node_output:
|
|
260
|
+
return False
|
|
261
|
+
|
|
262
|
+
if not isinstance(target_node_output.instance, BaseDescriptor):
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
return _contains_reference_to_output(target_node_output.instance, event.node_definition.Outputs.text)
|