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,437 @@
|
|
1
|
+
from vellum.workflows.edges.edge import Edge
|
2
|
+
from vellum.workflows.graph.graph import Graph
|
3
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
4
|
+
from vellum.workflows.ports.port import Port
|
5
|
+
|
6
|
+
|
7
|
+
def test_graph__from_node():
|
8
|
+
# GIVEN a node
|
9
|
+
class MyNode(BaseNode):
|
10
|
+
pass
|
11
|
+
|
12
|
+
# WHEN we create a graph from the node
|
13
|
+
graph = Graph.from_node(MyNode)
|
14
|
+
|
15
|
+
# THEN the graph has the node as the entrypoint
|
16
|
+
assert set(graph.entrypoints) == {MyNode}
|
17
|
+
|
18
|
+
# AND one node
|
19
|
+
assert len(list(graph.nodes)) == 1
|
20
|
+
|
21
|
+
# AND no edges
|
22
|
+
assert len(list(graph.edges)) == 0
|
23
|
+
|
24
|
+
|
25
|
+
def test_graph__from_edge():
|
26
|
+
# GIVEN an edge
|
27
|
+
class SourceNode(BaseNode):
|
28
|
+
pass
|
29
|
+
|
30
|
+
class TargetNode(BaseNode):
|
31
|
+
pass
|
32
|
+
|
33
|
+
edge = Edge(SourceNode.Ports.default, TargetNode)
|
34
|
+
|
35
|
+
# WHEN we create a graph from the edge
|
36
|
+
graph = Graph.from_edge(edge)
|
37
|
+
|
38
|
+
# THEN the graph has the source node as the entrypoint
|
39
|
+
assert set(graph.entrypoints) == {SourceNode}
|
40
|
+
|
41
|
+
# AND two nodes
|
42
|
+
assert len(list(graph.nodes)) == 2
|
43
|
+
|
44
|
+
# AND one edge
|
45
|
+
assert len(list(graph.edges)) == 1
|
46
|
+
|
47
|
+
|
48
|
+
def test_graph__node_to_node():
|
49
|
+
# GIVEN two nodes
|
50
|
+
class SourceNode(BaseNode):
|
51
|
+
pass
|
52
|
+
|
53
|
+
class TargetNode(BaseNode):
|
54
|
+
pass
|
55
|
+
|
56
|
+
# WHEN we create a graph from the source node to the target node
|
57
|
+
graph = SourceNode >> TargetNode
|
58
|
+
|
59
|
+
# THEN the graph has the source node as the entrypoint
|
60
|
+
assert set(graph.entrypoints) == {SourceNode}
|
61
|
+
|
62
|
+
# AND two nodes
|
63
|
+
assert len(list(graph.nodes)) == 2
|
64
|
+
|
65
|
+
# AND one edge
|
66
|
+
assert len(list(graph.edges)) == 1
|
67
|
+
|
68
|
+
|
69
|
+
def test_graph__port_to_node():
|
70
|
+
# GIVEN a two nodes, where the source node has a port
|
71
|
+
class SourceNode(BaseNode):
|
72
|
+
class Ports(BaseNode.Ports):
|
73
|
+
output = Port.on_else()
|
74
|
+
|
75
|
+
class TargetNode(BaseNode):
|
76
|
+
pass
|
77
|
+
|
78
|
+
# WHEN we create a graph from the source port to the target node
|
79
|
+
graph = SourceNode.Ports.output >> TargetNode
|
80
|
+
|
81
|
+
# THEN the graph has the source node as the entrypoint
|
82
|
+
assert set(graph.entrypoints) == {SourceNode}
|
83
|
+
|
84
|
+
# AND two nodes
|
85
|
+
assert len(list(graph.nodes)) == 2
|
86
|
+
|
87
|
+
# AND one edge
|
88
|
+
assert len(list(graph.edges)) == 1
|
89
|
+
|
90
|
+
|
91
|
+
def test_graph__graph_to_node():
|
92
|
+
# GIVEN three nodes
|
93
|
+
class SourceNode(BaseNode):
|
94
|
+
pass
|
95
|
+
|
96
|
+
class MiddleNode(BaseNode):
|
97
|
+
pass
|
98
|
+
|
99
|
+
class TargetNode(BaseNode):
|
100
|
+
pass
|
101
|
+
|
102
|
+
# WHEN we create a graph from the source node to the target node via the middle node
|
103
|
+
graph = SourceNode >> MiddleNode >> TargetNode
|
104
|
+
|
105
|
+
# THEN the graph has the source node as the entrypoint
|
106
|
+
assert set(graph.entrypoints) == {SourceNode}
|
107
|
+
|
108
|
+
# AND three nodes
|
109
|
+
assert len(list(graph.nodes)) == 3
|
110
|
+
|
111
|
+
# AND two edges
|
112
|
+
assert len(list(graph.edges)) == 2
|
113
|
+
|
114
|
+
|
115
|
+
def test_graph__edgeless_graph_to_node():
|
116
|
+
# GIVEN two nodes
|
117
|
+
class SourceNode(BaseNode):
|
118
|
+
pass
|
119
|
+
|
120
|
+
class TargetNode(BaseNode):
|
121
|
+
pass
|
122
|
+
|
123
|
+
# AND a graph of the first node
|
124
|
+
subgraph = Graph.from_node(SourceNode)
|
125
|
+
|
126
|
+
# WHEN we create a graph from the source graph to the target node
|
127
|
+
graph = subgraph >> TargetNode
|
128
|
+
|
129
|
+
# THEN the graph has the source node as the entrypoint
|
130
|
+
assert set(graph.entrypoints) == {SourceNode}
|
131
|
+
|
132
|
+
# AND two nodes
|
133
|
+
assert len(list(graph.nodes)) == 2
|
134
|
+
|
135
|
+
# AND one edge
|
136
|
+
assert len(list(graph.edges)) == 1
|
137
|
+
|
138
|
+
# AND the first node's port has reference to the edge
|
139
|
+
assert set(SourceNode.Ports.default.edges) == set(graph.edges)
|
140
|
+
|
141
|
+
|
142
|
+
def test_graph__edgeless_graph_to_graph():
|
143
|
+
# GIVEN two nodes
|
144
|
+
class SourceNode(BaseNode):
|
145
|
+
pass
|
146
|
+
|
147
|
+
class TargetNode(BaseNode):
|
148
|
+
pass
|
149
|
+
|
150
|
+
# AND a graph of the first node
|
151
|
+
subgraph = Graph.from_node(SourceNode)
|
152
|
+
|
153
|
+
# AND a graph of the second node
|
154
|
+
target_subgraph = Graph.from_node(TargetNode)
|
155
|
+
|
156
|
+
# WHEN we create a graph from the source graph to the target node
|
157
|
+
graph = subgraph >> target_subgraph
|
158
|
+
|
159
|
+
# THEN the graph has the source node as the entrypoint
|
160
|
+
assert set(graph.entrypoints) == {SourceNode}
|
161
|
+
|
162
|
+
# AND two nodes
|
163
|
+
assert len(list(graph.nodes)) == 2
|
164
|
+
|
165
|
+
# AND one edge
|
166
|
+
assert len(list(graph.edges)) == 1
|
167
|
+
|
168
|
+
# AND the first node's port has reference to the edges
|
169
|
+
assert set(SourceNode.Ports.default.edges) == set(graph.edges)
|
170
|
+
|
171
|
+
|
172
|
+
def test_graph__graph_to_edgeless_graph():
|
173
|
+
# GIVEN three nodes
|
174
|
+
class SourceNode(BaseNode):
|
175
|
+
pass
|
176
|
+
|
177
|
+
class MiddleNode(BaseNode):
|
178
|
+
pass
|
179
|
+
|
180
|
+
class TargetNode(BaseNode):
|
181
|
+
pass
|
182
|
+
|
183
|
+
# AND a graph of the first two nodes
|
184
|
+
subgraph = SourceNode >> MiddleNode
|
185
|
+
|
186
|
+
# AND a graph of the third node
|
187
|
+
target_subgraph = Graph.from_node(TargetNode)
|
188
|
+
|
189
|
+
# WHEN we create a graph from the source graph to the target node
|
190
|
+
graph = subgraph >> target_subgraph
|
191
|
+
|
192
|
+
# THEN the graph has the source node as the entrypoint
|
193
|
+
assert set(graph.entrypoints) == {SourceNode}
|
194
|
+
|
195
|
+
# AND three nodes
|
196
|
+
assert len(list(graph.nodes)) == 3
|
197
|
+
|
198
|
+
# AND two edges
|
199
|
+
assert len(list(graph.edges)) == 2
|
200
|
+
|
201
|
+
|
202
|
+
def test_graph__node_to_graph():
|
203
|
+
# GIVEN three nodes
|
204
|
+
class SourceNode(BaseNode):
|
205
|
+
pass
|
206
|
+
|
207
|
+
class MiddleNode(BaseNode):
|
208
|
+
pass
|
209
|
+
|
210
|
+
class TargetNode(BaseNode):
|
211
|
+
pass
|
212
|
+
|
213
|
+
# AND a graph of the last two nodes
|
214
|
+
target_subgraph = MiddleNode >> TargetNode
|
215
|
+
|
216
|
+
# WHEN we create a graph from the source node to the target graph
|
217
|
+
graph = SourceNode >> target_subgraph
|
218
|
+
|
219
|
+
# THEN the graph has the source node as the entrypoint
|
220
|
+
assert set(graph.entrypoints) == {SourceNode}
|
221
|
+
|
222
|
+
# AND three nodes
|
223
|
+
assert len(list(graph.nodes)) == 3
|
224
|
+
|
225
|
+
# AND two edges
|
226
|
+
assert len(list(graph.edges)) == 2
|
227
|
+
|
228
|
+
|
229
|
+
def test_graph__repeated_edge():
|
230
|
+
# GIVEN two nodes
|
231
|
+
class SourceNode(BaseNode):
|
232
|
+
pass
|
233
|
+
|
234
|
+
class TargetNode(BaseNode):
|
235
|
+
pass
|
236
|
+
|
237
|
+
# WHEN we create a graph from the source node to the target node twice
|
238
|
+
graph = SourceNode >> TargetNode >> SourceNode >> TargetNode
|
239
|
+
|
240
|
+
# THEN the graph has the source node as the entrypoint
|
241
|
+
assert set(graph.entrypoints) == {SourceNode}
|
242
|
+
|
243
|
+
# AND two nodes
|
244
|
+
assert len(list(graph.nodes)) == 2
|
245
|
+
|
246
|
+
# AND two edges
|
247
|
+
assert len(list(graph.edges)) == 2
|
248
|
+
|
249
|
+
# AND the first node's port has reference to just one edge
|
250
|
+
assert len(list(SourceNode.Ports.default.edges)) == 1
|
251
|
+
|
252
|
+
|
253
|
+
def test_graph__node_to_set():
|
254
|
+
# GIVEN three nodes, one with ports
|
255
|
+
class SourceNode(BaseNode):
|
256
|
+
pass
|
257
|
+
|
258
|
+
class MiddleNode(BaseNode):
|
259
|
+
class Ports(BaseNode.Ports):
|
260
|
+
top = Port.on_if(SourceNode.Execution.count.less_than(1))
|
261
|
+
bottom = Port.on_else()
|
262
|
+
|
263
|
+
class TargetNode(BaseNode):
|
264
|
+
pass
|
265
|
+
|
266
|
+
# WHEN we create a graph with a set
|
267
|
+
graph = SourceNode >> {
|
268
|
+
MiddleNode.Ports.top >> SourceNode,
|
269
|
+
MiddleNode.Ports.bottom >> TargetNode,
|
270
|
+
}
|
271
|
+
|
272
|
+
# THEN the graph has the source node as the entrypoint
|
273
|
+
assert set(graph.entrypoints) == {SourceNode}
|
274
|
+
|
275
|
+
# AND three nodes
|
276
|
+
assert len(list(graph.nodes)) == 3
|
277
|
+
|
278
|
+
# AND two edges
|
279
|
+
assert len(list(graph.edges)) == 3
|
280
|
+
|
281
|
+
|
282
|
+
def test_graph__graph_to_set():
|
283
|
+
# GIVEN four nodes, one with ports
|
284
|
+
class SourceNode(BaseNode):
|
285
|
+
pass
|
286
|
+
|
287
|
+
class SecondNode(BaseNode):
|
288
|
+
pass
|
289
|
+
|
290
|
+
class MiddleNode(BaseNode):
|
291
|
+
class Ports(BaseNode.Ports):
|
292
|
+
top = Port.on_if(SourceNode.Execution.count.less_than(1))
|
293
|
+
bottom = Port.on_else()
|
294
|
+
|
295
|
+
class TargetNode(BaseNode):
|
296
|
+
pass
|
297
|
+
|
298
|
+
# WHEN we create a graph between a graph and a set
|
299
|
+
graph = (
|
300
|
+
SourceNode
|
301
|
+
>> SecondNode
|
302
|
+
>> {
|
303
|
+
MiddleNode.Ports.top >> SourceNode,
|
304
|
+
MiddleNode.Ports.bottom >> TargetNode,
|
305
|
+
}
|
306
|
+
)
|
307
|
+
|
308
|
+
# THEN the graph has the source node as the entrypoint
|
309
|
+
assert set(graph.entrypoints) == {SourceNode}
|
310
|
+
|
311
|
+
# AND three nodes
|
312
|
+
assert len(list(graph.nodes)) == 4
|
313
|
+
|
314
|
+
# AND two edges
|
315
|
+
assert len(list(graph.edges)) == 4
|
316
|
+
|
317
|
+
|
318
|
+
def test_graph__graph_set_to_set():
|
319
|
+
# GIVEN five nodes
|
320
|
+
class SourceNode(BaseNode):
|
321
|
+
pass
|
322
|
+
|
323
|
+
class TopNode(BaseNode):
|
324
|
+
pass
|
325
|
+
|
326
|
+
class BottomNode(BaseNode):
|
327
|
+
pass
|
328
|
+
|
329
|
+
class ConditionalNode(BaseNode):
|
330
|
+
class Ports(BaseNode.Ports):
|
331
|
+
top = Port.on_if(SourceNode.Execution.count.less_than(1))
|
332
|
+
bottom = Port.on_else()
|
333
|
+
|
334
|
+
class EndNode(BaseNode):
|
335
|
+
pass
|
336
|
+
|
337
|
+
# WHEN we create a graph that draws an edge between sets
|
338
|
+
graph = (
|
339
|
+
SourceNode
|
340
|
+
>> {
|
341
|
+
TopNode,
|
342
|
+
BottomNode,
|
343
|
+
}
|
344
|
+
>> {
|
345
|
+
ConditionalNode.Ports.top >> SourceNode,
|
346
|
+
ConditionalNode.Ports.bottom >> EndNode,
|
347
|
+
}
|
348
|
+
)
|
349
|
+
|
350
|
+
# THEN the graph has the source node as the entrypoint
|
351
|
+
assert set(graph.entrypoints) == {SourceNode}
|
352
|
+
|
353
|
+
# AND three nodes
|
354
|
+
assert len(list(graph.nodes)) == 5
|
355
|
+
|
356
|
+
# AND two edges
|
357
|
+
assert len(list(graph.edges)) == 6
|
358
|
+
|
359
|
+
|
360
|
+
def test_graph__port_to_graph():
|
361
|
+
# GIVEN three nodes where the first node has a port
|
362
|
+
class SourceNode(BaseNode):
|
363
|
+
class Ports(BaseNode.Ports):
|
364
|
+
custom = Port.on_else()
|
365
|
+
|
366
|
+
class MiddleNode(BaseNode):
|
367
|
+
pass
|
368
|
+
|
369
|
+
class EndNode(BaseNode):
|
370
|
+
pass
|
371
|
+
|
372
|
+
# WHEN we create a graph from the port to a subgraph
|
373
|
+
graph = SourceNode.Ports.custom >> (MiddleNode >> EndNode)
|
374
|
+
|
375
|
+
# THEN the graph has the source node as the entrypoint
|
376
|
+
assert set(graph.entrypoints) == {SourceNode}
|
377
|
+
|
378
|
+
# AND three nodes
|
379
|
+
assert len(list(graph.nodes)) == 3
|
380
|
+
|
381
|
+
# AND two edges
|
382
|
+
assert len(list(graph.edges)) == 2
|
383
|
+
|
384
|
+
|
385
|
+
def test_graph__port_to_set():
|
386
|
+
# GIVEN three nodes where the first node has a port
|
387
|
+
class SourceNode(BaseNode):
|
388
|
+
class Ports(BaseNode.Ports):
|
389
|
+
custom = Port.on_else()
|
390
|
+
|
391
|
+
class TopNode(BaseNode):
|
392
|
+
pass
|
393
|
+
|
394
|
+
class BottomNode(BaseNode):
|
395
|
+
pass
|
396
|
+
|
397
|
+
# WHEN we create a graph from the port to the set
|
398
|
+
graph = SourceNode.Ports.custom >> {
|
399
|
+
TopNode,
|
400
|
+
BottomNode,
|
401
|
+
}
|
402
|
+
|
403
|
+
# THEN the graph has the source node as the entrypoint
|
404
|
+
assert set(graph.entrypoints) == {SourceNode}
|
405
|
+
|
406
|
+
# AND three nodes
|
407
|
+
assert len(list(graph.nodes)) == 3
|
408
|
+
|
409
|
+
# AND two edges
|
410
|
+
assert len(list(graph.edges)) == 2
|
411
|
+
|
412
|
+
|
413
|
+
def test_graph__set_to_node():
|
414
|
+
# GIVEN three nodes
|
415
|
+
class TopNode(BaseNode):
|
416
|
+
pass
|
417
|
+
|
418
|
+
class BottomNode(BaseNode):
|
419
|
+
pass
|
420
|
+
|
421
|
+
class TargetNode(BaseNode):
|
422
|
+
pass
|
423
|
+
|
424
|
+
# WHEN we create a graph from a set to a node
|
425
|
+
graph = {
|
426
|
+
TopNode,
|
427
|
+
BottomNode,
|
428
|
+
} >> TargetNode
|
429
|
+
|
430
|
+
# THEN the graph has both the top node and the bottom node as the entrypoints
|
431
|
+
assert set(graph.entrypoints) == {TopNode, BottomNode}
|
432
|
+
|
433
|
+
# AND three nodes
|
434
|
+
assert len(list(graph.nodes)) == 3
|
435
|
+
|
436
|
+
# AND two edges
|
437
|
+
assert len(list(graph.edges)) == 2
|
@@ -0,0 +1,55 @@
|
|
1
|
+
from typing import Any, Iterator, Tuple, Type
|
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.references import ExternalInputReference, WorkflowInputReference
|
8
|
+
from vellum.workflows.references.input import InputReference
|
9
|
+
from vellum.workflows.types.utils import get_class_attr_names, infer_types
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass_transform(kw_only_default=True)
|
13
|
+
class _BaseInputsMeta(type):
|
14
|
+
def __getattribute__(cls, name: str) -> Any:
|
15
|
+
if not name.startswith("_") and name in cls.__annotations__ and issubclass(cls, BaseInputs):
|
16
|
+
instance = vars(cls).get(name)
|
17
|
+
types = infer_types(cls, name)
|
18
|
+
|
19
|
+
if getattr(cls, "__descriptor_class__", None) is ExternalInputReference:
|
20
|
+
return ExternalInputReference(name=name, types=types, instance=instance, inputs_class=cls)
|
21
|
+
else:
|
22
|
+
return WorkflowInputReference(name=name, types=types, instance=instance, inputs_class=cls)
|
23
|
+
|
24
|
+
return super().__getattribute__(name)
|
25
|
+
|
26
|
+
def __iter__(cls) -> Iterator[InputReference]:
|
27
|
+
# We iterate through the inheritance hierarchy to find all the WorkflowInputReference attached to this
|
28
|
+
# Inputs class. __mro__ is the method resolution order, which is the order in which base classes are resolved.
|
29
|
+
for resolved_cls in cls.__mro__:
|
30
|
+
attr_names = get_class_attr_names(resolved_cls)
|
31
|
+
for attr_name in attr_names:
|
32
|
+
attr_value = getattr(resolved_cls, attr_name)
|
33
|
+
if not isinstance(attr_value, (WorkflowInputReference, ExternalInputReference)):
|
34
|
+
continue
|
35
|
+
|
36
|
+
yield attr_value
|
37
|
+
|
38
|
+
|
39
|
+
class BaseInputs(metaclass=_BaseInputsMeta):
|
40
|
+
__parent_class__: Type = type(None)
|
41
|
+
|
42
|
+
def __init__(self, **kwargs: Any) -> None:
|
43
|
+
for name, value in kwargs.items():
|
44
|
+
setattr(self, name, value)
|
45
|
+
|
46
|
+
def __iter__(self) -> Iterator[Tuple[InputReference, Any]]:
|
47
|
+
for input_descriptor in self.__class__:
|
48
|
+
if hasattr(self, input_descriptor.name):
|
49
|
+
yield (input_descriptor, getattr(self, input_descriptor.name))
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def __get_pydantic_core_schema__(
|
53
|
+
cls, source_type: Type[Any], handler: GetCoreSchemaHandler
|
54
|
+
) -> core_schema.CoreSchema:
|
55
|
+
return core_schema.is_instance_schema(cls)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
|
4
|
+
|
5
|
+
def load_logger() -> logging.Logger:
|
6
|
+
logger = logging.getLogger(__package__)
|
7
|
+
logger.setLevel(os.getenv("LOG_LEVEL", logging.DEBUG))
|
8
|
+
|
9
|
+
# Add a stream handler so that we see logs in the console when running tests
|
10
|
+
handler = logging.StreamHandler()
|
11
|
+
handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
|
12
|
+
logger.addHandler(handler)
|
13
|
+
|
14
|
+
return logger
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from vellum.workflows.nodes.bases import BaseNode
|
2
|
+
from vellum.workflows.nodes.core import ErrorNode, InlineSubworkflowNode, MapNode, RetryNode, TemplatingNode
|
3
|
+
from vellum.workflows.nodes.displayable import (
|
4
|
+
APINode,
|
5
|
+
CodeExecutionNode,
|
6
|
+
ConditionalNode,
|
7
|
+
FinalOutputNode,
|
8
|
+
GuardrailNode,
|
9
|
+
InlinePromptNode,
|
10
|
+
PromptDeploymentNode,
|
11
|
+
SearchNode,
|
12
|
+
SubworkflowDeploymentNode,
|
13
|
+
)
|
14
|
+
from vellum.workflows.nodes.displayable.bases import (
|
15
|
+
BaseInlinePromptNode as BaseInlinePromptNode,
|
16
|
+
BasePromptDeploymentNode as BasePromptDeploymentNode,
|
17
|
+
BaseSearchNode as BaseSearchNode,
|
18
|
+
)
|
19
|
+
|
20
|
+
__all__ = [
|
21
|
+
# Base
|
22
|
+
"BaseNode",
|
23
|
+
# Core
|
24
|
+
"ErrorNode",
|
25
|
+
"InlineSubworkflowNode",
|
26
|
+
"MapNode",
|
27
|
+
"RetryNode",
|
28
|
+
"TemplatingNode",
|
29
|
+
# Vellum Base Nodes
|
30
|
+
"BaseSearchNode",
|
31
|
+
"BaseInlinePromptNode",
|
32
|
+
"BasePromptDeploymentNode",
|
33
|
+
# Vellum Nodes
|
34
|
+
"APINode",
|
35
|
+
"CodeExecutionNode",
|
36
|
+
"GuardrailNode",
|
37
|
+
"InlinePromptNode",
|
38
|
+
"PromptDeploymentNode",
|
39
|
+
"SearchNode",
|
40
|
+
"ConditionalNode",
|
41
|
+
"GuardrailNode",
|
42
|
+
"SubworkflowDeploymentNode",
|
43
|
+
"FinalOutputNode",
|
44
|
+
"PromptDeploymentNode",
|
45
|
+
"SearchNode",
|
46
|
+
]
|