vellum-ai 0.9.16rc2__py3-none-any.whl → 0.9.16rc4__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.
- vellum/plugins/__init__.py +0 -0
- vellum/plugins/pydantic.py +74 -0
- vellum/plugins/utils.py +19 -0
- vellum/plugins/vellum_mypy.py +639 -3
- vellum/workflows/README.md +90 -0
- vellum/workflows/__init__.py +5 -0
- vellum/workflows/constants.py +43 -0
- vellum/workflows/descriptors/__init__.py +0 -0
- vellum/workflows/descriptors/base.py +339 -0
- vellum/workflows/descriptors/tests/test_utils.py +83 -0
- vellum/workflows/descriptors/utils.py +90 -0
- vellum/workflows/edges/__init__.py +5 -0
- vellum/workflows/edges/edge.py +23 -0
- vellum/workflows/emitters/__init__.py +5 -0
- vellum/workflows/emitters/base.py +14 -0
- vellum/workflows/environment/__init__.py +5 -0
- vellum/workflows/environment/environment.py +7 -0
- vellum/workflows/errors/__init__.py +6 -0
- vellum/workflows/errors/types.py +20 -0
- vellum/workflows/events/__init__.py +31 -0
- vellum/workflows/events/node.py +125 -0
- vellum/workflows/events/tests/__init__.py +0 -0
- vellum/workflows/events/tests/test_event.py +216 -0
- vellum/workflows/events/types.py +52 -0
- vellum/workflows/events/utils.py +5 -0
- vellum/workflows/events/workflow.py +139 -0
- vellum/workflows/exceptions.py +15 -0
- vellum/workflows/expressions/__init__.py +0 -0
- vellum/workflows/expressions/accessor.py +52 -0
- vellum/workflows/expressions/and_.py +32 -0
- vellum/workflows/expressions/begins_with.py +31 -0
- vellum/workflows/expressions/between.py +38 -0
- vellum/workflows/expressions/coalesce_expression.py +41 -0
- vellum/workflows/expressions/contains.py +30 -0
- vellum/workflows/expressions/does_not_begin_with.py +31 -0
- vellum/workflows/expressions/does_not_contain.py +30 -0
- vellum/workflows/expressions/does_not_end_with.py +31 -0
- vellum/workflows/expressions/does_not_equal.py +25 -0
- vellum/workflows/expressions/ends_with.py +31 -0
- vellum/workflows/expressions/equals.py +25 -0
- vellum/workflows/expressions/greater_than.py +33 -0
- vellum/workflows/expressions/greater_than_or_equal_to.py +33 -0
- vellum/workflows/expressions/in_.py +31 -0
- vellum/workflows/expressions/is_blank.py +24 -0
- vellum/workflows/expressions/is_not_blank.py +24 -0
- vellum/workflows/expressions/is_not_null.py +21 -0
- vellum/workflows/expressions/is_not_undefined.py +22 -0
- vellum/workflows/expressions/is_null.py +21 -0
- vellum/workflows/expressions/is_undefined.py +22 -0
- vellum/workflows/expressions/less_than.py +33 -0
- vellum/workflows/expressions/less_than_or_equal_to.py +33 -0
- vellum/workflows/expressions/not_between.py +38 -0
- vellum/workflows/expressions/not_in.py +31 -0
- vellum/workflows/expressions/or_.py +32 -0
- vellum/workflows/graph/__init__.py +3 -0
- vellum/workflows/graph/graph.py +131 -0
- vellum/workflows/graph/tests/__init__.py +0 -0
- vellum/workflows/graph/tests/test_graph.py +437 -0
- vellum/workflows/inputs/__init__.py +5 -0
- vellum/workflows/inputs/base.py +55 -0
- vellum/workflows/logging.py +14 -0
- vellum/workflows/nodes/__init__.py +46 -0
- vellum/workflows/nodes/bases/__init__.py +7 -0
- vellum/workflows/nodes/bases/base.py +332 -0
- vellum/workflows/nodes/bases/base_subworkflow_node/__init__.py +5 -0
- vellum/workflows/nodes/bases/base_subworkflow_node/node.py +10 -0
- vellum/workflows/nodes/bases/tests/__init__.py +0 -0
- vellum/workflows/nodes/bases/tests/test_base_node.py +125 -0
- vellum/workflows/nodes/core/__init__.py +16 -0
- vellum/workflows/nodes/core/error_node/__init__.py +5 -0
- vellum/workflows/nodes/core/error_node/node.py +26 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py +5 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +73 -0
- vellum/workflows/nodes/core/map_node/__init__.py +5 -0
- vellum/workflows/nodes/core/map_node/node.py +147 -0
- vellum/workflows/nodes/core/map_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/core/map_node/tests/test_node.py +65 -0
- vellum/workflows/nodes/core/retry_node/__init__.py +5 -0
- vellum/workflows/nodes/core/retry_node/node.py +106 -0
- vellum/workflows/nodes/core/retry_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/core/retry_node/tests/test_node.py +93 -0
- vellum/workflows/nodes/core/templating_node/__init__.py +5 -0
- vellum/workflows/nodes/core/templating_node/custom_filters.py +12 -0
- vellum/workflows/nodes/core/templating_node/exceptions.py +2 -0
- vellum/workflows/nodes/core/templating_node/node.py +123 -0
- vellum/workflows/nodes/core/templating_node/render.py +55 -0
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +21 -0
- vellum/workflows/nodes/core/try_node/__init__.py +5 -0
- vellum/workflows/nodes/core/try_node/node.py +110 -0
- vellum/workflows/nodes/core/try_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/core/try_node/tests/test_node.py +82 -0
- vellum/workflows/nodes/displayable/__init__.py +31 -0
- vellum/workflows/nodes/displayable/api_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/api_node/node.py +44 -0
- vellum/workflows/nodes/displayable/bases/__init__.py +11 -0
- vellum/workflows/nodes/displayable/bases/api_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/bases/api_node/node.py +70 -0
- vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +60 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py +13 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +118 -0
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +98 -0
- vellum/workflows/nodes/displayable/bases/search_node.py +90 -0
- vellum/workflows/nodes/displayable/code_execution_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/code_execution_node/node.py +197 -0
- vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py +0 -0
- vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py +3 -0
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +111 -0
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +10 -0
- vellum/workflows/nodes/displayable/conditional_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/conditional_node/node.py +25 -0
- vellum/workflows/nodes/displayable/final_output_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/final_output_node/node.py +43 -0
- vellum/workflows/nodes/displayable/guardrail_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/guardrail_node/node.py +97 -0
- vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +41 -0
- vellum/workflows/nodes/displayable/merge_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/merge_node/node.py +10 -0
- vellum/workflows/nodes/displayable/prompt_deployment_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +45 -0
- vellum/workflows/nodes/displayable/search_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/search_node/node.py +26 -0
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/__init__.py +5 -0
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +156 -0
- vellum/workflows/nodes/displayable/tests/__init__.py +0 -0
- vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +148 -0
- vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py +134 -0
- vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +80 -0
- vellum/workflows/nodes/utils.py +27 -0
- vellum/workflows/outputs/__init__.py +6 -0
- vellum/workflows/outputs/base.py +196 -0
- vellum/workflows/ports/__init__.py +7 -0
- vellum/workflows/ports/node_ports.py +75 -0
- vellum/workflows/ports/port.py +75 -0
- vellum/workflows/ports/utils.py +40 -0
- vellum/workflows/references/__init__.py +17 -0
- vellum/workflows/references/environment_variable.py +20 -0
- vellum/workflows/references/execution_count.py +20 -0
- vellum/workflows/references/external_input.py +49 -0
- vellum/workflows/references/input.py +7 -0
- vellum/workflows/references/lazy.py +55 -0
- vellum/workflows/references/node.py +43 -0
- vellum/workflows/references/output.py +78 -0
- vellum/workflows/references/state_value.py +23 -0
- vellum/workflows/references/vellum_secret.py +15 -0
- vellum/workflows/references/workflow_input.py +41 -0
- vellum/workflows/resolvers/__init__.py +5 -0
- vellum/workflows/resolvers/base.py +15 -0
- vellum/workflows/runner/__init__.py +5 -0
- vellum/workflows/runner/runner.py +588 -0
- vellum/workflows/runner/types.py +18 -0
- vellum/workflows/state/__init__.py +5 -0
- vellum/workflows/state/base.py +327 -0
- vellum/workflows/state/context.py +18 -0
- vellum/workflows/state/encoder.py +57 -0
- vellum/workflows/state/store.py +28 -0
- vellum/workflows/state/tests/__init__.py +0 -0
- vellum/workflows/state/tests/test_state.py +113 -0
- vellum/workflows/types/__init__.py +0 -0
- vellum/workflows/types/core.py +91 -0
- vellum/workflows/types/generics.py +14 -0
- vellum/workflows/types/stack.py +39 -0
- vellum/workflows/types/tests/__init__.py +0 -0
- vellum/workflows/types/tests/test_utils.py +76 -0
- vellum/workflows/types/utils.py +164 -0
- vellum/workflows/utils/__init__.py +0 -0
- vellum/workflows/utils/names.py +13 -0
- vellum/workflows/utils/tests/__init__.py +0 -0
- vellum/workflows/utils/tests/test_names.py +15 -0
- vellum/workflows/utils/tests/test_vellum_variables.py +25 -0
- vellum/workflows/utils/vellum_variables.py +81 -0
- vellum/workflows/vellum_client.py +18 -0
- vellum/workflows/workflows/__init__.py +5 -0
- vellum/workflows/workflows/base.py +365 -0
- {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/METADATA +2 -1
- {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/RECORD +245 -7
- vellum_cli/__init__.py +72 -0
- vellum_cli/aliased_group.py +103 -0
- vellum_cli/config.py +96 -0
- vellum_cli/image_push.py +112 -0
- vellum_cli/logger.py +36 -0
- vellum_cli/pull.py +73 -0
- vellum_cli/push.py +121 -0
- vellum_cli/tests/test_config.py +100 -0
- vellum_cli/tests/test_pull.py +152 -0
- vellum_ee/workflows/__init__.py +0 -0
- vellum_ee/workflows/display/__init__.py +0 -0
- vellum_ee/workflows/display/base.py +73 -0
- vellum_ee/workflows/display/nodes/__init__.py +4 -0
- vellum_ee/workflows/display/nodes/base_node_display.py +116 -0
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +36 -0
- vellum_ee/workflows/display/nodes/get_node_display_class.py +25 -0
- vellum_ee/workflows/display/nodes/tests/__init__.py +0 -0
- vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +47 -0
- vellum_ee/workflows/display/nodes/types.py +18 -0
- vellum_ee/workflows/display/nodes/utils.py +33 -0
- vellum_ee/workflows/display/nodes/vellum/__init__.py +32 -0
- vellum_ee/workflows/display/nodes/vellum/api_node.py +205 -0
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +71 -0
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py +217 -0
- vellum_ee/workflows/display/nodes/vellum/final_output_node.py +61 -0
- vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +49 -0
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +170 -0
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +99 -0
- vellum_ee/workflows/display/nodes/vellum/map_node.py +100 -0
- vellum_ee/workflows/display/nodes/vellum/merge_node.py +48 -0
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +68 -0
- vellum_ee/workflows/display/nodes/vellum/search_node.py +193 -0
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +58 -0
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +67 -0
- vellum_ee/workflows/display/nodes/vellum/tests/__init__.py +0 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +106 -0
- vellum_ee/workflows/display/nodes/vellum/try_node.py +38 -0
- vellum_ee/workflows/display/nodes/vellum/utils.py +76 -0
- vellum_ee/workflows/display/tests/__init__.py +0 -0
- vellum_ee/workflows/display/tests/workflow_serialization/__init__.py +0 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +426 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +607 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +1175 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +235 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +511 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +372 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +272 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +289 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +354 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +123 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +84 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +233 -0
- vellum_ee/workflows/display/types.py +46 -0
- vellum_ee/workflows/display/utils/__init__.py +0 -0
- vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
- vellum_ee/workflows/display/utils/tests/test_uuids.py +16 -0
- vellum_ee/workflows/display/utils/uuids.py +24 -0
- vellum_ee/workflows/display/utils/vellum.py +121 -0
- vellum_ee/workflows/display/vellum.py +357 -0
- vellum_ee/workflows/display/workflows/__init__.py +5 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +302 -0
- vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +32 -0
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +386 -0
- {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/LICENSE +0 -0
- {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/WHEEL +0 -0
- {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,196 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Generic, Iterator, Set, Tuple, Type, TypeVar, Union, cast
|
2
|
+
from typing_extensions import dataclass_transform
|
3
|
+
|
4
|
+
from pydantic import GetCoreSchemaHandler
|
5
|
+
from pydantic_core import core_schema
|
6
|
+
|
7
|
+
from vellum.workflows.constants import UNDEF
|
8
|
+
from vellum.workflows.references.output import OutputReference
|
9
|
+
from vellum.workflows.types.utils import get_class_attr_names, infer_types
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
13
|
+
|
14
|
+
_Delta = TypeVar("_Delta")
|
15
|
+
_Accumulated = TypeVar("_Accumulated")
|
16
|
+
|
17
|
+
|
18
|
+
class BaseOutput(Generic[_Delta, _Accumulated]):
|
19
|
+
_value: Union[_Accumulated, Type[UNDEF]]
|
20
|
+
_delta: Union[_Delta, Type[UNDEF]]
|
21
|
+
_name: str
|
22
|
+
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
name: str,
|
26
|
+
value: Union[_Accumulated, Type[UNDEF]] = UNDEF,
|
27
|
+
delta: Union[_Delta, Type[UNDEF]] = UNDEF,
|
28
|
+
) -> None:
|
29
|
+
if value is not UNDEF and delta is not UNDEF:
|
30
|
+
raise ValueError("Cannot set both value and delta")
|
31
|
+
|
32
|
+
self._name = name
|
33
|
+
self._value = value
|
34
|
+
self._delta = delta
|
35
|
+
|
36
|
+
@property
|
37
|
+
def delta(self) -> Union[_Delta, Type[UNDEF]]:
|
38
|
+
return self._delta
|
39
|
+
|
40
|
+
@property
|
41
|
+
def value(self) -> Union[_Accumulated, Type[UNDEF]]:
|
42
|
+
return self._value
|
43
|
+
|
44
|
+
@property
|
45
|
+
def is_initiated(self) -> bool:
|
46
|
+
return self._delta is UNDEF and self._value is UNDEF
|
47
|
+
|
48
|
+
@property
|
49
|
+
def is_streaming(self) -> bool:
|
50
|
+
return self._delta is not UNDEF and self._value is UNDEF
|
51
|
+
|
52
|
+
@property
|
53
|
+
def is_fulfilled(self) -> bool:
|
54
|
+
return self._delta is UNDEF and self._value is not UNDEF
|
55
|
+
|
56
|
+
@property
|
57
|
+
def name(self) -> str:
|
58
|
+
return self._name
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def __get_pydantic_core_schema__(
|
62
|
+
cls, source_type: Type[Any], handler: GetCoreSchemaHandler
|
63
|
+
) -> core_schema.CoreSchema:
|
64
|
+
return core_schema.is_instance_schema(cls)
|
65
|
+
|
66
|
+
def serialize(self) -> dict:
|
67
|
+
data: dict[str, Any] = {
|
68
|
+
"name": self.name,
|
69
|
+
}
|
70
|
+
|
71
|
+
if self.value is not UNDEF:
|
72
|
+
data["value"] = self.value
|
73
|
+
|
74
|
+
if self.delta is not UNDEF:
|
75
|
+
data["delta"] = self.delta
|
76
|
+
|
77
|
+
return data
|
78
|
+
|
79
|
+
|
80
|
+
@dataclass_transform(kw_only_default=True)
|
81
|
+
class _BaseOutputsMeta(type):
|
82
|
+
def __eq__(cls, other: Any) -> bool:
|
83
|
+
"""
|
84
|
+
We need to include custom eq logic to prevent infinite loops during ipython reloading.
|
85
|
+
"""
|
86
|
+
|
87
|
+
if not isinstance(other, _BaseOutputsMeta):
|
88
|
+
return False
|
89
|
+
|
90
|
+
if not cls.__qualname__.endswith(".Outputs") or not other.__qualname__.endswith(".Outputs"):
|
91
|
+
return super().__eq__(other)
|
92
|
+
|
93
|
+
self_outputs_class = cast(Type["BaseNode.Outputs"], cls)
|
94
|
+
other_outputs_class = cast(Type["BaseNode.Outputs"], other)
|
95
|
+
|
96
|
+
if not hasattr(self_outputs_class, "_node_class") or not hasattr(other_outputs_class, "_node_class"):
|
97
|
+
return super().__eq__(other)
|
98
|
+
|
99
|
+
if self_outputs_class._node_class is None or other_outputs_class._node_class is None:
|
100
|
+
return super().__eq__(other)
|
101
|
+
|
102
|
+
return getattr(self_outputs_class._node_class, "__qualname__") == getattr(
|
103
|
+
other_outputs_class._node_class, "__qualname__"
|
104
|
+
)
|
105
|
+
|
106
|
+
def __setattr__(cls, name: str, value: Any) -> None:
|
107
|
+
if isinstance(value, OutputReference):
|
108
|
+
# During module reload, tools like ipython will set class attributes from the get attribute
|
109
|
+
# from the old class. Our getattribute dynamically returns OutputDescriptors, which means during
|
110
|
+
# module reload, it will override the class' original default value with its own OutputReference.
|
111
|
+
# We want to avoid this, so we check if the name of the class and the name of the descriptor
|
112
|
+
# are the same, and if they are, we don't set the attribute.
|
113
|
+
if f"{cls.__qualname__}.{name}" == str(value):
|
114
|
+
return super().__setattr__(name, value.instance)
|
115
|
+
|
116
|
+
return super().__setattr__(name, value)
|
117
|
+
|
118
|
+
def __getattribute__(cls, name: str) -> Any:
|
119
|
+
if name.startswith("_") or not issubclass(cls, BaseOutputs):
|
120
|
+
return super().__getattribute__(name)
|
121
|
+
|
122
|
+
attr_names = get_class_attr_names(cls)
|
123
|
+
if name in attr_names:
|
124
|
+
# We first try to resolve the instance that this class attribute name is mapped to. If it's not found,
|
125
|
+
# we iterate through its inheritance hierarchy to find the first base class that has this attribute
|
126
|
+
# and use its mapping.
|
127
|
+
instance = vars(cls).get(name, UNDEF)
|
128
|
+
if not instance:
|
129
|
+
for base in cls.__mro__[1:]:
|
130
|
+
if hasattr(base, name):
|
131
|
+
instance = getattr(base, name)
|
132
|
+
break
|
133
|
+
|
134
|
+
types = infer_types(cls, name)
|
135
|
+
return OutputReference(
|
136
|
+
name=name,
|
137
|
+
types=types,
|
138
|
+
instance=instance,
|
139
|
+
outputs_class=cls,
|
140
|
+
)
|
141
|
+
|
142
|
+
return super().__getattribute__(name)
|
143
|
+
|
144
|
+
def __hash__(self) -> int:
|
145
|
+
return hash(self.__qualname__)
|
146
|
+
|
147
|
+
def __iter__(cls) -> Iterator[OutputReference]:
|
148
|
+
# We iterate through the inheritance hierarchy to find all the OutputDescriptors attached to this Outputs class.
|
149
|
+
# __mro__ is the method resolution order, which is the order in which base classes are resolved.
|
150
|
+
yielded_attr_names: Set[str] = set()
|
151
|
+
|
152
|
+
for resolved_cls in cls.__mro__:
|
153
|
+
attr_names = get_class_attr_names(resolved_cls)
|
154
|
+
for attr_name in attr_names:
|
155
|
+
if attr_name in yielded_attr_names:
|
156
|
+
continue
|
157
|
+
|
158
|
+
attr_value = getattr(resolved_cls, attr_name)
|
159
|
+
if not isinstance(attr_value, OutputReference):
|
160
|
+
continue
|
161
|
+
|
162
|
+
yield attr_value
|
163
|
+
yielded_attr_names.add(attr_name)
|
164
|
+
|
165
|
+
|
166
|
+
class BaseOutputs(metaclass=_BaseOutputsMeta):
|
167
|
+
def __init__(self, **kwargs: Any) -> None:
|
168
|
+
for name, value in kwargs.items():
|
169
|
+
setattr(self, name, value)
|
170
|
+
|
171
|
+
if hasattr(self, "_outputs_post_init") and callable(self._outputs_post_init):
|
172
|
+
self._outputs_post_init(**kwargs)
|
173
|
+
|
174
|
+
def __eq__(self, other: object) -> bool:
|
175
|
+
if not isinstance(other, dict):
|
176
|
+
return super().__eq__(other)
|
177
|
+
|
178
|
+
outputs = {name: value for name, value in vars(self).items() if not name.startswith("_") and value is not UNDEF}
|
179
|
+
return outputs == other
|
180
|
+
|
181
|
+
def __repr__(self) -> str:
|
182
|
+
values = f"{', '.join(f'{k}={v}' for k, v in vars(self).items() if not k.startswith('_'))}"
|
183
|
+
return f"{self.__class__.__name__}({values})"
|
184
|
+
|
185
|
+
def __iter__(self) -> Iterator[Tuple[OutputReference, Any]]:
|
186
|
+
for output_descriptor in self.__class__:
|
187
|
+
yield (output_descriptor, getattr(self, output_descriptor.name, output_descriptor.instance))
|
188
|
+
|
189
|
+
def __getitem__(self, key: str) -> Any:
|
190
|
+
return getattr(self, key)
|
191
|
+
|
192
|
+
@classmethod
|
193
|
+
def __get_pydantic_core_schema__(
|
194
|
+
cls, source_type: Type[Any], handler: GetCoreSchemaHandler
|
195
|
+
) -> core_schema.CoreSchema:
|
196
|
+
return core_schema.is_instance_schema(cls)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
from typing import Any, Dict, Iterable, Iterator, Optional, Set, Tuple, Type
|
2
|
+
|
3
|
+
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
4
|
+
from vellum.workflows.ports.port import Port
|
5
|
+
from vellum.workflows.ports.utils import validate_ports
|
6
|
+
from vellum.workflows.state.base import BaseState
|
7
|
+
from vellum.workflows.types.core import ConditionType
|
8
|
+
|
9
|
+
|
10
|
+
class _NodePortsMeta(type):
|
11
|
+
def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
|
12
|
+
for k, v in dct.items():
|
13
|
+
if k.startswith("_"):
|
14
|
+
continue
|
15
|
+
if not isinstance(v, Port):
|
16
|
+
raise ValueError(f"All fields in {name} must be of type Port. Received: {v.__class__}")
|
17
|
+
|
18
|
+
return super().__new__(mcs, name, bases, dct)
|
19
|
+
|
20
|
+
def __iter__(cls) -> Iterator[Port]:
|
21
|
+
for attr_name, attr_value in cls.__dict__.items():
|
22
|
+
if not attr_name.startswith("_") and isinstance(attr_value, Port):
|
23
|
+
yield attr_value
|
24
|
+
|
25
|
+
@property
|
26
|
+
def _default_port(cls) -> Optional[Port]:
|
27
|
+
default_ports = [port for port in cls if port.default]
|
28
|
+
|
29
|
+
if len(default_ports) > 1:
|
30
|
+
raise ValueError(f"Class {cls.__name__} must have only one default port")
|
31
|
+
|
32
|
+
return default_ports[0] if default_ports else None
|
33
|
+
|
34
|
+
|
35
|
+
class NodePorts(metaclass=_NodePortsMeta):
|
36
|
+
def __call__(self, outputs: BaseOutputs, state: BaseState) -> Iterable[Port]:
|
37
|
+
"""
|
38
|
+
Invokes the appropriate ports based on the fulfilled outputs and state.
|
39
|
+
"""
|
40
|
+
|
41
|
+
invoked_ports: Set[Port] = set()
|
42
|
+
all_ports = [port for port in self.__class__]
|
43
|
+
enforce_single_invoked_conditional_port = validate_ports(all_ports)
|
44
|
+
|
45
|
+
for port in all_ports:
|
46
|
+
if port._condition_type == ConditionType.IF:
|
47
|
+
resolved_condition = port.resolve_condition(state)
|
48
|
+
if resolved_condition:
|
49
|
+
invoked_ports.add(port)
|
50
|
+
if enforce_single_invoked_conditional_port:
|
51
|
+
break
|
52
|
+
|
53
|
+
elif port._condition_type == ConditionType.ELIF:
|
54
|
+
resolved_condition = port.resolve_condition(state)
|
55
|
+
if resolved_condition:
|
56
|
+
invoked_ports.add(port)
|
57
|
+
break
|
58
|
+
|
59
|
+
elif port._condition_type == ConditionType.ELSE and not invoked_ports:
|
60
|
+
invoked_ports.add(port)
|
61
|
+
break
|
62
|
+
|
63
|
+
if not invoked_ports:
|
64
|
+
default_port = self.__class__._default_port
|
65
|
+
if default_port:
|
66
|
+
invoked_ports.add(default_port)
|
67
|
+
|
68
|
+
return invoked_ports
|
69
|
+
|
70
|
+
def __lt__(self, output: BaseOutput) -> Iterable[Port]:
|
71
|
+
"""
|
72
|
+
Invokes the appropriate ports based on the streamed output
|
73
|
+
"""
|
74
|
+
|
75
|
+
return set()
|
@@ -0,0 +1,75 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Iterator, List, Optional, Type
|
2
|
+
|
3
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
4
|
+
from vellum.workflows.edges.edge import Edge
|
5
|
+
from vellum.workflows.graph import Graph, GraphTarget
|
6
|
+
from vellum.workflows.state.base import BaseState
|
7
|
+
from vellum.workflows.types.core import ConditionType
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from vellum.workflows.nodes.bases import BaseNode
|
11
|
+
|
12
|
+
|
13
|
+
class Port:
|
14
|
+
node_class: Type["BaseNode"]
|
15
|
+
|
16
|
+
_edges: List[Edge]
|
17
|
+
_condition: Optional[BaseDescriptor]
|
18
|
+
_condition_type: Optional[ConditionType]
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
default: bool = False,
|
23
|
+
fork_state: bool = False,
|
24
|
+
condition: Optional[Any] = None,
|
25
|
+
condition_type: Optional[ConditionType] = None,
|
26
|
+
):
|
27
|
+
self.default = default
|
28
|
+
self.node_class = None # type: ignore[assignment]
|
29
|
+
self._fork_state = fork_state
|
30
|
+
self._edges = []
|
31
|
+
self._condition: Optional[BaseDescriptor] = condition
|
32
|
+
self._condition_type: Optional[ConditionType] = condition_type
|
33
|
+
|
34
|
+
def __set_name__(self, owner: Type, name: str) -> None:
|
35
|
+
self.name = name
|
36
|
+
|
37
|
+
def __repr__(self) -> str:
|
38
|
+
return f"{self.node_class}.Ports.{self.name}"
|
39
|
+
|
40
|
+
@property
|
41
|
+
def fork_state(self) -> bool:
|
42
|
+
return self._fork_state
|
43
|
+
|
44
|
+
@property
|
45
|
+
def edges(self) -> Iterator[Edge]:
|
46
|
+
return iter(self._edges)
|
47
|
+
|
48
|
+
def __rshift__(self, other: GraphTarget) -> Graph:
|
49
|
+
if isinstance(other, set) or isinstance(other, Graph):
|
50
|
+
return Graph.from_port(self) >> other
|
51
|
+
|
52
|
+
edge = Edge(from_port=self, to_node=other)
|
53
|
+
if edge not in self._edges:
|
54
|
+
self._edges.append(edge)
|
55
|
+
|
56
|
+
return Graph.from_edge(edge)
|
57
|
+
|
58
|
+
@staticmethod
|
59
|
+
def on_if(condition: BaseDescriptor, fork_state: bool = False) -> "Port":
|
60
|
+
return Port(condition=condition, condition_type=ConditionType.IF, fork_state=fork_state)
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def on_elif(condition: BaseDescriptor, fork_state: bool = False) -> "Port":
|
64
|
+
return Port(condition=condition, condition_type=ConditionType.ELIF, fork_state=fork_state)
|
65
|
+
|
66
|
+
@staticmethod
|
67
|
+
def on_else(fork_state: bool = False) -> "Port":
|
68
|
+
return Port(condition_type=ConditionType.ELSE, fork_state=fork_state)
|
69
|
+
|
70
|
+
def resolve_condition(self, state: BaseState) -> bool:
|
71
|
+
if self._condition is None:
|
72
|
+
return False
|
73
|
+
|
74
|
+
value = self._condition.resolve(state)
|
75
|
+
return bool(value)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from collections import Counter
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
from vellum.workflows.ports.port import Port
|
5
|
+
from vellum.workflows.types.core import ConditionType
|
6
|
+
|
7
|
+
PORT_TYPE_PRIORITIES = {
|
8
|
+
ConditionType.IF: 1,
|
9
|
+
ConditionType.ELIF: 2,
|
10
|
+
ConditionType.ELSE: 3,
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
def validate_ports(ports: List[Port]) -> bool:
|
15
|
+
# We don't want to validate ports with no condition (default ports)
|
16
|
+
port_types = [port._condition_type for port in ports if port._condition_type is not None]
|
17
|
+
sorted_port_types = sorted(port_types, key=lambda port_type: PORT_TYPE_PRIORITIES[port_type])
|
18
|
+
|
19
|
+
if sorted_port_types != port_types:
|
20
|
+
raise ValueError("Port conditions must be in the following order: on_if, on_elif, on_else")
|
21
|
+
|
22
|
+
counter = Counter(port_types)
|
23
|
+
number_of_if_ports = counter[ConditionType.IF]
|
24
|
+
number_of_elif_ports = counter[ConditionType.ELIF]
|
25
|
+
number_of_else_ports = counter[ConditionType.ELSE]
|
26
|
+
ports_class = f"{ports[0].node_class}.Ports"
|
27
|
+
|
28
|
+
if number_of_if_ports == 0 and (number_of_elif_ports > 0 or number_of_else_ports > 0):
|
29
|
+
raise ValueError(
|
30
|
+
f"Class {ports_class} containing on_elif or on_else port conditions must have at least one on_if condition"
|
31
|
+
)
|
32
|
+
|
33
|
+
if number_of_elif_ports > 0 and number_of_if_ports != 1:
|
34
|
+
raise ValueError(f"Class {ports_class} containing on_elif ports must have exactly one on_if condition")
|
35
|
+
|
36
|
+
if number_of_else_ports > 1:
|
37
|
+
raise ValueError(f"Class {ports_class} must have at most one on_else port condition")
|
38
|
+
|
39
|
+
enforce_single_invoked_conditional_port = number_of_elif_ports > 0 or number_of_if_ports <= 1
|
40
|
+
return enforce_single_invoked_conditional_port
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from .environment_variable import EnvironmentVariableReference
|
2
|
+
from .external_input import ExternalInputReference
|
3
|
+
from .lazy import LazyReference
|
4
|
+
from .node import NodeReference
|
5
|
+
from .output import OutputReference
|
6
|
+
from .state_value import StateValueReference
|
7
|
+
from .workflow_input import WorkflowInputReference
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"EnvironmentVariableReference",
|
11
|
+
"ExternalInputReference",
|
12
|
+
"LazyReference",
|
13
|
+
"NodeReference",
|
14
|
+
"OutputReference",
|
15
|
+
"StateValueReference",
|
16
|
+
"WorkflowInputReference",
|
17
|
+
]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import os
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from vellum.workflows.state.base import BaseState
|
8
|
+
|
9
|
+
|
10
|
+
class EnvironmentVariableReference(BaseDescriptor[str]):
|
11
|
+
def __init__(self, *, name: str) -> None:
|
12
|
+
super().__init__(name=name, types=(str,))
|
13
|
+
|
14
|
+
def resolve(self, state: "BaseState") -> str:
|
15
|
+
env_value = os.environ.get(self.name)
|
16
|
+
if env_value is not None:
|
17
|
+
return env_value
|
18
|
+
|
19
|
+
# Fetch Vellum Environment Variable named `self.name` once that project is done
|
20
|
+
raise ValueError(f"No environment variable named '{self.name}' found")
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Type
|
2
|
+
|
3
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from vellum.workflows.nodes.bases import BaseNode
|
7
|
+
from vellum.workflows.state.base import BaseState
|
8
|
+
|
9
|
+
|
10
|
+
class ExecutionCountReference(BaseDescriptor[int]):
|
11
|
+
|
12
|
+
def __init__(
|
13
|
+
self,
|
14
|
+
node_class: Type["BaseNode"],
|
15
|
+
) -> None:
|
16
|
+
super().__init__(name=f"Execution Count({node_class.__name__})", types=(int,))
|
17
|
+
self._node_class = node_class
|
18
|
+
|
19
|
+
def resolve(self, state: "BaseState") -> int:
|
20
|
+
return state.meta.node_execution_cache.get_execution_count(self._node_class)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Generic, Optional, Tuple, Type, TypeVar, cast
|
2
|
+
|
3
|
+
from pydantic import GetCoreSchemaHandler
|
4
|
+
from pydantic_core import core_schema
|
5
|
+
|
6
|
+
from vellum.workflows.constants import UNDEF
|
7
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
8
|
+
from vellum.workflows.errors.types import VellumErrorCode
|
9
|
+
from vellum.workflows.exceptions import NodeException
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from vellum.workflows.inputs.base import BaseInputs
|
13
|
+
from vellum.workflows.state.base import BaseState
|
14
|
+
|
15
|
+
_InputType = TypeVar("_InputType")
|
16
|
+
|
17
|
+
|
18
|
+
class ExternalInputReference(BaseDescriptor[_InputType], Generic[_InputType]):
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
*,
|
23
|
+
name: str,
|
24
|
+
types: Tuple[Type[_InputType], ...],
|
25
|
+
instance: Optional[_InputType],
|
26
|
+
inputs_class: Type["BaseInputs"],
|
27
|
+
) -> None:
|
28
|
+
super().__init__(name=name, types=types, instance=instance)
|
29
|
+
self._inputs_class = inputs_class
|
30
|
+
|
31
|
+
@property
|
32
|
+
def inputs_class(self) -> Type["BaseInputs"]:
|
33
|
+
return self._inputs_class
|
34
|
+
|
35
|
+
def resolve(self, state: "BaseState") -> _InputType:
|
36
|
+
external_input = state.meta.external_inputs.get(self)
|
37
|
+
if external_input is not UNDEF:
|
38
|
+
return cast(_InputType, external_input)
|
39
|
+
|
40
|
+
if state.meta.parent:
|
41
|
+
return self.resolve(state.meta.parent)
|
42
|
+
|
43
|
+
raise NodeException(f"Missing required Node Input: {self._name}", code=VellumErrorCode.INVALID_INPUTS)
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def __get_pydantic_core_schema__(
|
47
|
+
cls, source_type: Type[Any], handler: GetCoreSchemaHandler
|
48
|
+
) -> core_schema.CoreSchema:
|
49
|
+
return core_schema.is_instance_schema(cls)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
from typing import Union
|
2
|
+
|
3
|
+
from vellum.workflows.references.external_input import ExternalInputReference
|
4
|
+
from vellum.workflows.references.workflow_input import WorkflowInputReference
|
5
|
+
|
6
|
+
# Note: We may want to consolidate these two into a single descriptor.
|
7
|
+
InputReference = Union[ExternalInputReference, WorkflowInputReference]
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import ast
|
2
|
+
import inspect
|
3
|
+
from typing import TYPE_CHECKING, Callable, Generic, TypeVar, get_args
|
4
|
+
|
5
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from vellum.workflows.state.base import BaseState
|
9
|
+
|
10
|
+
_T = TypeVar("_T")
|
11
|
+
|
12
|
+
|
13
|
+
class LazyReference(BaseDescriptor[_T], Generic[_T]):
|
14
|
+
def __init__(
|
15
|
+
self,
|
16
|
+
get: Callable[[], BaseDescriptor[_T]],
|
17
|
+
) -> None:
|
18
|
+
self._get = get
|
19
|
+
# TODO: figure out this some times returns empty
|
20
|
+
# Original example: https://github.com/vellum-ai/workflows-as-code-runner-prototype/pull/128/files#diff-67aaa468aa37b6130756bfaf93f03954d7b518617922efb3350882ea4ae03d60R36 # noqa: E501
|
21
|
+
# https://app.shortcut.com/vellum/story/4993
|
22
|
+
types = get_args(type(self))
|
23
|
+
super().__init__(name=self._get_name(), types=types)
|
24
|
+
|
25
|
+
def resolve(self, state: "BaseState") -> _T:
|
26
|
+
from vellum.workflows.descriptors.utils import resolve_value
|
27
|
+
|
28
|
+
return resolve_value(self._get(), state)
|
29
|
+
|
30
|
+
def _get_name(self) -> str:
|
31
|
+
"""
|
32
|
+
We try our best to parse out the source code that generates the descriptor,
|
33
|
+
setting that as the descriptor's name. Names are only used for debugging, so
|
34
|
+
we could flesh out edge cases over time.
|
35
|
+
"""
|
36
|
+
source = inspect.getsource(self._get).strip()
|
37
|
+
try:
|
38
|
+
parsed = ast.parse(source)
|
39
|
+
assignment = parsed.body[0]
|
40
|
+
|
41
|
+
if not isinstance(assignment, ast.Assign):
|
42
|
+
return source
|
43
|
+
|
44
|
+
call = assignment.value
|
45
|
+
if not isinstance(call, ast.Call) or not call.args:
|
46
|
+
return source
|
47
|
+
|
48
|
+
lambda_expression = call.args[0]
|
49
|
+
if not isinstance(lambda_expression, ast.Lambda):
|
50
|
+
return source
|
51
|
+
|
52
|
+
body = lambda_expression.body
|
53
|
+
return source[body.col_offset : body.end_col_offset]
|
54
|
+
except Exception:
|
55
|
+
return source
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Optional, Tuple, Type, TypeVar
|
2
|
+
|
3
|
+
from pydantic import GetCoreSchemaHandler
|
4
|
+
from pydantic_core import core_schema
|
5
|
+
|
6
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
7
|
+
from vellum.workflows.errors.types import VellumErrorCode
|
8
|
+
from vellum.workflows.exceptions import NodeException
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
12
|
+
from vellum.workflows.state.base import BaseState
|
13
|
+
|
14
|
+
_T = TypeVar("_T")
|
15
|
+
|
16
|
+
|
17
|
+
class NodeReference(BaseDescriptor[_T]):
|
18
|
+
def __init__(
|
19
|
+
self, *, name: str, types: Tuple[Type[_T], ...], instance: Optional[_T] = None, node_class: Type["BaseNode"]
|
20
|
+
) -> None:
|
21
|
+
self._name = name
|
22
|
+
self._types = types
|
23
|
+
self._instance = instance
|
24
|
+
self._node_class = node_class
|
25
|
+
|
26
|
+
@property
|
27
|
+
def node_class(self) -> Type["BaseNode"]:
|
28
|
+
return self._node_class
|
29
|
+
|
30
|
+
def resolve(self, state: "BaseState") -> _T:
|
31
|
+
raise NodeException(
|
32
|
+
f"NodeDescriptors cannot be resolved during runtime. Got: {self._name}",
|
33
|
+
code=VellumErrorCode.INTERNAL_ERROR,
|
34
|
+
)
|
35
|
+
|
36
|
+
def __repr__(self) -> str:
|
37
|
+
return f"{self._node_class.__qualname__}.{self._name}"
|
38
|
+
|
39
|
+
@classmethod
|
40
|
+
def __get_pydantic_core_schema__(
|
41
|
+
cls, source_type: Type[Any], handler: GetCoreSchemaHandler
|
42
|
+
) -> core_schema.CoreSchema:
|
43
|
+
return core_schema.is_instance_schema(cls)
|