vellum-ai 0.14.25__py3-none-any.whl → 0.14.27__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/__init__.py +6 -4
- vellum/client/__init__.py +4 -0
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/core/jsonable_encoder.py +1 -1
- vellum/client/resources/__init__.py +2 -2
- vellum/client/resources/prompts/__init__.py +2 -0
- vellum/client/resources/prompts/client.py +197 -0
- vellum/client/resources/workflows/__init__.py +0 -3
- vellum/client/resources/workflows/client.py +0 -9
- vellum/client/types/__init__.py +4 -2
- vellum/client/types/deployment_release_tag_read.py +7 -1
- vellum/client/types/prompt_exec_config.py +37 -0
- vellum/client/types/{release.py → release_tag_release.py} +1 -1
- vellum/client/types/workflow_release_tag_read.py +2 -2
- vellum/client/types/workflow_release_tag_workflow_deployment_history_item.py +3 -10
- vellum/{types/release.py → resources/prompts/__init__.py} +1 -1
- vellum/resources/{workflows/types/__init__.py → prompts/client.py} +1 -1
- vellum/{resources/workflows/types/workflows_pull_request_format.py → types/prompt_exec_config.py} +1 -1
- vellum/types/release_tag_release.py +3 -0
- vellum/workflows/events/types.py +10 -7
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +2 -4
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +2 -4
- vellum/workflows/nodes/displayable/conftest.py +117 -0
- vellum/workflows/nodes/displayable/guardrail_node/node.py +10 -11
- vellum/workflows/nodes/displayable/guardrail_node/test_node.py +38 -0
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +49 -0
- vellum/workflows/nodes/displayable/prompt_deployment_node/tests/test_node.py +49 -0
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +2 -5
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py +63 -0
- vellum/workflows/references/workflow_input.py +3 -0
- vellum/workflows/runner/runner.py +2 -0
- {vellum_ai-0.14.25.dist-info → vellum_ai-0.14.27.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.25.dist-info → vellum_ai-0.14.27.dist-info}/RECORD +44 -40
- vellum_ee/workflows/display/base.py +13 -7
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py +11 -10
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +1 -1
- vellum_ee/workflows/display/types.py +5 -9
- vellum_ee/workflows/display/vellum.py +9 -4
- vellum_ee/workflows/display/workflows/base_workflow_display.py +20 -21
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +7 -35
- vellum_ee/workflows/tests/test_server.py +54 -0
- vellum/client/resources/workflows/types/__init__.py +0 -5
- vellum/client/resources/workflows/types/workflows_pull_request_format.py +0 -5
- {vellum_ai-0.14.25.dist-info → vellum_ai-0.14.27.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.25.dist-info → vellum_ai-0.14.27.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.25.dist-info → vellum_ai-0.14.27.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
import pytest
|
2
|
+
from uuid import UUID
|
3
|
+
|
4
|
+
from vellum.workflows.events.types import (
|
5
|
+
CodeResourceDefinition,
|
6
|
+
NodeParentContext,
|
7
|
+
WorkflowDeploymentParentContext,
|
8
|
+
WorkflowParentContext,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
@pytest.fixture
|
13
|
+
def mock_complex_parent_context():
|
14
|
+
# TODO: We were able to confirm that this parent context caused our serialization to hang, but don't know why yet.
|
15
|
+
# We should try to reduce this example further to isolate a minimal example that reproduces the issue.
|
16
|
+
return NodeParentContext(
|
17
|
+
span_id=UUID("d697f8c8-b363-4154-8469-eb4f9fb5e445"),
|
18
|
+
parent=WorkflowParentContext(
|
19
|
+
span_id=UUID("a0c68884-22c3-4ac9-8476-8747884d80e1"),
|
20
|
+
parent=NodeParentContext(
|
21
|
+
span_id=UUID("46163407-71f7-40f2-9f66-872d4b338fcc"),
|
22
|
+
parent=WorkflowParentContext(
|
23
|
+
span_id=UUID("0ddf01e7-d0c3-426c-af27-d8bfb22fcdd5"),
|
24
|
+
parent=NodeParentContext(
|
25
|
+
span_id=UUID("79a6c926-b5f3-4ede-b2f8-4bb6f0c086ba"),
|
26
|
+
parent=WorkflowParentContext(
|
27
|
+
span_id=UUID("530a56fe-90fd-4f4c-b905-457975fb3e10"),
|
28
|
+
parent=WorkflowDeploymentParentContext(
|
29
|
+
span_id=UUID("3e10a8c2-558c-4ef7-926d-7b79ebc7cba9"),
|
30
|
+
parent=NodeParentContext(
|
31
|
+
span_id=UUID("a3cd4086-c0b9-4dff-88f3-3e2191b8a2a7"),
|
32
|
+
parent=WorkflowParentContext(
|
33
|
+
span_id=UUID("c2ba7577-8d24-49b1-aa92-b9ace8244090"),
|
34
|
+
workflow_definition=CodeResourceDefinition(
|
35
|
+
id=UUID("2e2d5c56-49b7-48b5-82fa-e80e72768b9c"),
|
36
|
+
name="Workflow",
|
37
|
+
module=["e81a6124-2c57-4c39-938c-ab6059059ff2", "workflow"],
|
38
|
+
),
|
39
|
+
),
|
40
|
+
node_definition=CodeResourceDefinition(
|
41
|
+
id=UUID("23d25675-f377-4450-916f-39ebee5c8ea9"),
|
42
|
+
name="SubworkflowDeployment",
|
43
|
+
module=[
|
44
|
+
"e81a6124-2c57-4c39-938c-ab6059059ff2",
|
45
|
+
"nodes",
|
46
|
+
"subworkflow_deployment",
|
47
|
+
],
|
48
|
+
),
|
49
|
+
),
|
50
|
+
deployment_id=UUID("cfc99610-2869-4506-b106-3fd7ce0bbb15"),
|
51
|
+
deployment_name="my-deployment",
|
52
|
+
deployment_history_item_id=UUID("13f31aae-29fd-4066-a4ec-c7687faebae3"),
|
53
|
+
release_tag_id=UUID("2d03987a-dcb5-49b9-981e-5e871c8f5d97"),
|
54
|
+
release_tag_name="LATEST",
|
55
|
+
external_id=None,
|
56
|
+
metadata=None,
|
57
|
+
workflow_version_id=UUID("7eaae816-b5f3-436d-8597-e8c3e4a32958"),
|
58
|
+
),
|
59
|
+
workflow_definition=CodeResourceDefinition(
|
60
|
+
id=UUID("2e2d5c56-49b7-48b5-82fa-e80e72768b9c"),
|
61
|
+
name="Workflow",
|
62
|
+
module=["3e10a8c2-558c-4ef7-926d-7b79ebc7cba9", "workflow"],
|
63
|
+
),
|
64
|
+
),
|
65
|
+
node_definition=CodeResourceDefinition(
|
66
|
+
id=UUID("42c8adc2-a0d6-499e-81a4-e2e02d7beba9"),
|
67
|
+
name="MyNode",
|
68
|
+
module=[
|
69
|
+
"3e10a8c2-558c-4ef7-926d-7b79ebc7cba9",
|
70
|
+
"nodes",
|
71
|
+
"my_node",
|
72
|
+
],
|
73
|
+
),
|
74
|
+
),
|
75
|
+
workflow_definition=CodeResourceDefinition(
|
76
|
+
id=UUID("b8563da0-7fd4-42e0-a75e-9ef037fca5a1"),
|
77
|
+
name="MyNodeWorkflow",
|
78
|
+
module=[
|
79
|
+
"3e10a8c2-558c-4ef7-926d-7b79ebc7cba9",
|
80
|
+
"nodes",
|
81
|
+
"my_node",
|
82
|
+
"workflow",
|
83
|
+
],
|
84
|
+
),
|
85
|
+
),
|
86
|
+
node_definition=CodeResourceDefinition(
|
87
|
+
id=UUID("d44aee53-3b6e-41fd-8b7a-908cb2c77821"),
|
88
|
+
name="RetryNode",
|
89
|
+
module=[
|
90
|
+
"3e10a8c2-558c-4ef7-926d-7b79ebc7cba9",
|
91
|
+
"nodes",
|
92
|
+
"my_node",
|
93
|
+
"nodes",
|
94
|
+
"my_prompt",
|
95
|
+
"MyPrompt",
|
96
|
+
"<adornment>",
|
97
|
+
],
|
98
|
+
),
|
99
|
+
),
|
100
|
+
workflow_definition=CodeResourceDefinition(
|
101
|
+
id=UUID("568a28dd-7134-436e-a5f4-790675212b51"),
|
102
|
+
name="Subworkflow",
|
103
|
+
module=["vellum", "workflows", "nodes", "utils"],
|
104
|
+
),
|
105
|
+
),
|
106
|
+
node_definition=CodeResourceDefinition(
|
107
|
+
id=UUID("86a34e5c-2652-49f0-9f9e-c653cf70029a"),
|
108
|
+
name="MyPrompt",
|
109
|
+
module=[
|
110
|
+
"3e10a8c2-558c-4ef7-926d-7b79ebc7cba9",
|
111
|
+
"nodes",
|
112
|
+
"my_node",
|
113
|
+
"nodes",
|
114
|
+
"my_prompt",
|
115
|
+
],
|
116
|
+
),
|
117
|
+
)
|
@@ -52,19 +52,18 @@ class GuardrailNode(BaseNode[StateType], Generic[StateType]):
|
|
52
52
|
message="Metric execution must have one output named 'score' with type 'float'",
|
53
53
|
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
54
54
|
)
|
55
|
-
|
56
|
-
log = metric_outputs.get("log")
|
57
|
-
|
58
|
-
if log is not None and not isinstance(log, str):
|
59
|
-
raise NodeException(
|
60
|
-
message="Metric execution log output must be of type 'str'",
|
61
|
-
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
62
|
-
)
|
63
|
-
if log:
|
64
|
-
metric_outputs.pop("log")
|
65
|
-
|
66
55
|
metric_outputs.pop("score")
|
67
56
|
|
57
|
+
if "log" in metric_outputs:
|
58
|
+
log = metric_outputs.pop("log") or ""
|
59
|
+
if not isinstance(log, str):
|
60
|
+
raise NodeException(
|
61
|
+
message="Metric execution log output must be of type 'str'",
|
62
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
63
|
+
)
|
64
|
+
else:
|
65
|
+
log = None
|
66
|
+
|
68
67
|
return self.Outputs(score=score, log=log, **metric_outputs)
|
69
68
|
|
70
69
|
def _compile_metric_inputs(self) -> List[MetricDefinitionInput]:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from vellum import TestSuiteRunMetricNumberOutput
|
4
|
+
from vellum.client.types.metric_definition_execution import MetricDefinitionExecution
|
5
|
+
from vellum.client.types.test_suite_run_metric_string_output import TestSuiteRunMetricStringOutput
|
6
|
+
from vellum.workflows.nodes.displayable.guardrail_node.node import GuardrailNode
|
7
|
+
|
8
|
+
|
9
|
+
@pytest.mark.parametrize("log_value", [None, ""], ids=["None", "Empty"])
|
10
|
+
def test_run_guardrail_node__empty_log(vellum_client, log_value):
|
11
|
+
"""Confirm that we can successfully invoke a Guardrail Node"""
|
12
|
+
|
13
|
+
# GIVEN a Guardrail Node
|
14
|
+
class MyGuard(GuardrailNode):
|
15
|
+
metric_definition = "example_metric_definition"
|
16
|
+
metric_inputs = {}
|
17
|
+
|
18
|
+
# AND we know that the guardrail node will return a blank log
|
19
|
+
mock_metric_execution = MetricDefinitionExecution(
|
20
|
+
outputs=[
|
21
|
+
TestSuiteRunMetricNumberOutput(
|
22
|
+
name="score",
|
23
|
+
value=0.6,
|
24
|
+
),
|
25
|
+
TestSuiteRunMetricStringOutput(
|
26
|
+
name="log",
|
27
|
+
value=log_value,
|
28
|
+
),
|
29
|
+
],
|
30
|
+
)
|
31
|
+
vellum_client.metric_definitions.execute_metric_definition.return_value = mock_metric_execution
|
32
|
+
|
33
|
+
# WHEN we run the Guardrail Node
|
34
|
+
outputs = MyGuard().run()
|
35
|
+
|
36
|
+
# THEN the workflow should have completed successfully
|
37
|
+
assert outputs.score == 0.6
|
38
|
+
assert outputs.log == ""
|
@@ -1,8 +1,11 @@
|
|
1
1
|
import pytest
|
2
2
|
from dataclasses import dataclass
|
3
|
+
import json
|
3
4
|
from uuid import uuid4
|
4
5
|
from typing import Any, Iterator, List
|
5
6
|
|
7
|
+
from httpx import Response
|
8
|
+
|
6
9
|
from vellum.client.core.api_error import ApiError
|
7
10
|
from vellum.client.core.pydantic_utilities import UniversalBaseModel
|
8
11
|
from vellum.client.types.chat_message import ChatMessage
|
@@ -17,6 +20,7 @@ from vellum.client.types.prompt_output import PromptOutput
|
|
17
20
|
from vellum.client.types.prompt_request_chat_history_input import PromptRequestChatHistoryInput
|
18
21
|
from vellum.client.types.prompt_request_json_input import PromptRequestJsonInput
|
19
22
|
from vellum.client.types.string_vellum_value import StringVellumValue
|
23
|
+
from vellum.workflows.context import execution_context
|
20
24
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
21
25
|
from vellum.workflows.exceptions import NodeException
|
22
26
|
from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
|
@@ -230,3 +234,48 @@ def test_inline_prompt_node__chat_history_inputs(vellum_adhoc_prompt_client):
|
|
230
234
|
),
|
231
235
|
]
|
232
236
|
assert mock_api.call_args.kwargs["input_variables"][0].type == "CHAT_HISTORY"
|
237
|
+
|
238
|
+
|
239
|
+
@pytest.mark.timeout(5)
|
240
|
+
def test_inline_prompt_node__parent_context(mock_httpx_transport, mock_complex_parent_context):
|
241
|
+
# GIVEN a prompt node
|
242
|
+
class MyNode(InlinePromptNode):
|
243
|
+
ml_model = "gpt-4o"
|
244
|
+
blocks = []
|
245
|
+
prompt_inputs = {}
|
246
|
+
|
247
|
+
# AND a known response from the httpx client
|
248
|
+
expected_outputs: List[PromptOutput] = [
|
249
|
+
StringVellumValue(value="Test"),
|
250
|
+
]
|
251
|
+
execution_id = str(uuid4())
|
252
|
+
events: List[ExecutePromptEvent] = [
|
253
|
+
InitiatedExecutePromptEvent(execution_id=execution_id),
|
254
|
+
FulfilledExecutePromptEvent(
|
255
|
+
execution_id=execution_id,
|
256
|
+
outputs=expected_outputs,
|
257
|
+
),
|
258
|
+
]
|
259
|
+
text = "\n".join(e.model_dump_json() for e in events)
|
260
|
+
|
261
|
+
mock_httpx_transport.handle_request.return_value = Response(
|
262
|
+
status_code=200,
|
263
|
+
text=text,
|
264
|
+
)
|
265
|
+
|
266
|
+
# WHEN the node is run with the complex parent context
|
267
|
+
trace_id = uuid4()
|
268
|
+
with execution_context(
|
269
|
+
parent_context=mock_complex_parent_context,
|
270
|
+
trace_id=trace_id,
|
271
|
+
):
|
272
|
+
outputs = list(MyNode().run())
|
273
|
+
|
274
|
+
# THEN the last output is as expected
|
275
|
+
assert outputs[-1].value == "Test"
|
276
|
+
|
277
|
+
# AND the prompt is executed with the correct execution context
|
278
|
+
call_request_args = mock_httpx_transport.handle_request.call_args_list[0][0][0]
|
279
|
+
request_execution_context = json.loads(call_request_args.read().decode("utf-8"))["execution_context"]
|
280
|
+
assert request_execution_context["trace_id"] == str(trace_id)
|
281
|
+
assert request_execution_context["parent_context"]
|
@@ -1,7 +1,10 @@
|
|
1
1
|
import pytest
|
2
|
+
import json
|
2
3
|
from uuid import uuid4
|
3
4
|
from typing import Any, Iterator, List
|
4
5
|
|
6
|
+
from httpx import Response
|
7
|
+
|
5
8
|
from vellum.client.types.chat_history_input_request import ChatHistoryInputRequest
|
6
9
|
from vellum.client.types.chat_message import ChatMessage
|
7
10
|
from vellum.client.types.chat_message_request import ChatMessageRequest
|
@@ -9,7 +12,9 @@ from vellum.client.types.execute_prompt_event import ExecutePromptEvent
|
|
9
12
|
from vellum.client.types.fulfilled_execute_prompt_event import FulfilledExecutePromptEvent
|
10
13
|
from vellum.client.types.initiated_execute_prompt_event import InitiatedExecutePromptEvent
|
11
14
|
from vellum.client.types.json_input_request import JsonInputRequest
|
15
|
+
from vellum.client.types.prompt_output import PromptOutput
|
12
16
|
from vellum.client.types.string_vellum_value import StringVellumValue
|
17
|
+
from vellum.workflows.context import execution_context
|
13
18
|
from vellum.workflows.nodes.displayable.prompt_deployment_node.node import PromptDeploymentNode
|
14
19
|
|
15
20
|
|
@@ -94,3 +99,47 @@ def test_run_node__any_array_input(vellum_client):
|
|
94
99
|
assert call_kwargs["inputs"] == [
|
95
100
|
JsonInputRequest(name="fruits", value=["apple", "banana", "cherry"]),
|
96
101
|
]
|
102
|
+
|
103
|
+
|
104
|
+
@pytest.mark.timeout(5)
|
105
|
+
def test_prompt_deployment_node__parent_context_serialization(mock_httpx_transport, mock_complex_parent_context):
|
106
|
+
# GIVEN a prompt deployment node
|
107
|
+
class MyNode(PromptDeploymentNode):
|
108
|
+
deployment = "example_prompt_deployment"
|
109
|
+
prompt_inputs = {}
|
110
|
+
|
111
|
+
# AND a known response from the httpx client
|
112
|
+
expected_outputs: List[PromptOutput] = [
|
113
|
+
StringVellumValue(value="Test"),
|
114
|
+
]
|
115
|
+
execution_id = str(uuid4())
|
116
|
+
events: List[ExecutePromptEvent] = [
|
117
|
+
InitiatedExecutePromptEvent(execution_id=execution_id),
|
118
|
+
FulfilledExecutePromptEvent(
|
119
|
+
execution_id=execution_id,
|
120
|
+
outputs=expected_outputs,
|
121
|
+
),
|
122
|
+
]
|
123
|
+
text = "\n".join(e.model_dump_json() for e in events)
|
124
|
+
|
125
|
+
mock_httpx_transport.handle_request.return_value = Response(
|
126
|
+
status_code=200,
|
127
|
+
text=text,
|
128
|
+
)
|
129
|
+
|
130
|
+
# WHEN the node is run with a complex parent context
|
131
|
+
trace_id = uuid4()
|
132
|
+
with execution_context(
|
133
|
+
parent_context=mock_complex_parent_context,
|
134
|
+
trace_id=trace_id,
|
135
|
+
):
|
136
|
+
outputs = list(MyNode().run())
|
137
|
+
|
138
|
+
# THEN the last output is as expected
|
139
|
+
assert outputs[-1].value == "Test"
|
140
|
+
|
141
|
+
# AND the prompt is executed with the correct execution context
|
142
|
+
call_request_args = mock_httpx_transport.handle_request.call_args_list[0][0][0]
|
143
|
+
request_execution_context = json.loads(call_request_args.read().decode("utf-8"))["execution_context"]
|
144
|
+
assert request_execution_context["trace_id"] == str(trace_id)
|
145
|
+
assert request_execution_context["parent_context"]
|
@@ -122,13 +122,10 @@ class SubworkflowDeploymentNode(BaseNode[StateType], Generic[StateType]):
|
|
122
122
|
return compiled_inputs
|
123
123
|
|
124
124
|
def run(self) -> Iterator[BaseOutput]:
|
125
|
-
|
126
|
-
parent_context = (
|
127
|
-
current_context.parent_context.model_dump(mode="json") if current_context.parent_context else None
|
128
|
-
)
|
125
|
+
execution_context = get_execution_context()
|
129
126
|
request_options = self.request_options or RequestOptions()
|
130
127
|
request_options["additional_body_parameters"] = {
|
131
|
-
"execution_context":
|
128
|
+
"execution_context": execution_context.model_dump(mode="json"),
|
132
129
|
**request_options.get("additional_body_parameters", {}),
|
133
130
|
}
|
134
131
|
|
@@ -1,8 +1,11 @@
|
|
1
1
|
import pytest
|
2
2
|
from datetime import datetime
|
3
|
+
import json
|
3
4
|
from uuid import uuid4
|
4
5
|
from typing import Any, Iterator, List
|
5
6
|
|
7
|
+
from httpx import Response
|
8
|
+
|
6
9
|
from vellum.client.core.api_error import ApiError
|
7
10
|
from vellum.client.types.chat_message import ChatMessage
|
8
11
|
from vellum.client.types.chat_message_request import ChatMessageRequest
|
@@ -13,6 +16,7 @@ from vellum.client.types.workflow_request_json_input_request import WorkflowRequ
|
|
13
16
|
from vellum.client.types.workflow_request_number_input_request import WorkflowRequestNumberInputRequest
|
14
17
|
from vellum.client.types.workflow_result_event import WorkflowResultEvent
|
15
18
|
from vellum.client.types.workflow_stream_event import WorkflowStreamEvent
|
19
|
+
from vellum.workflows.context import execution_context
|
16
20
|
from vellum.workflows.errors import WorkflowErrorCode
|
17
21
|
from vellum.workflows.exceptions import NodeException
|
18
22
|
from vellum.workflows.nodes.displayable.subworkflow_deployment_node.node import SubworkflowDeploymentNode
|
@@ -405,3 +409,62 @@ def test_subworkflow_deployment_node__immediate_api_error__node_exception(vellum
|
|
405
409
|
# THEN the node raises the correct NodeException
|
406
410
|
assert e.value.code == WorkflowErrorCode.INVALID_INPUTS
|
407
411
|
assert e.value.message == "Not found"
|
412
|
+
|
413
|
+
|
414
|
+
@pytest.mark.timeout(5)
|
415
|
+
def test_prompt_deployment_node__parent_context_serialization(mock_httpx_transport, mock_complex_parent_context):
|
416
|
+
# GIVEN a prompt deployment node
|
417
|
+
class MyNode(SubworkflowDeploymentNode):
|
418
|
+
deployment = "example_subworkflow_deployment"
|
419
|
+
subworkflow_inputs = {}
|
420
|
+
|
421
|
+
# AND a known response from the httpx client
|
422
|
+
execution_id = str(uuid4())
|
423
|
+
events: List[WorkflowStreamEvent] = [
|
424
|
+
WorkflowExecutionWorkflowResultEvent(
|
425
|
+
execution_id=execution_id,
|
426
|
+
data=WorkflowResultEvent(
|
427
|
+
id=str(uuid4()),
|
428
|
+
state="INITIATED",
|
429
|
+
ts=datetime.now(),
|
430
|
+
),
|
431
|
+
),
|
432
|
+
WorkflowExecutionWorkflowResultEvent(
|
433
|
+
execution_id=execution_id,
|
434
|
+
data=WorkflowResultEvent(
|
435
|
+
id=str(uuid4()),
|
436
|
+
state="FULFILLED",
|
437
|
+
ts=datetime.now(),
|
438
|
+
outputs=[
|
439
|
+
WorkflowOutputString(
|
440
|
+
id=str(uuid4()),
|
441
|
+
name="final-output_copy", # Note the hyphen here
|
442
|
+
value="Test",
|
443
|
+
)
|
444
|
+
],
|
445
|
+
),
|
446
|
+
),
|
447
|
+
]
|
448
|
+
text = "\n".join(e.model_dump_json() for e in events)
|
449
|
+
|
450
|
+
mock_httpx_transport.handle_request.return_value = Response(
|
451
|
+
status_code=200,
|
452
|
+
text=text,
|
453
|
+
)
|
454
|
+
|
455
|
+
# WHEN the node is run with a complex parent context
|
456
|
+
trace_id = uuid4()
|
457
|
+
with execution_context(
|
458
|
+
parent_context=mock_complex_parent_context,
|
459
|
+
trace_id=trace_id,
|
460
|
+
):
|
461
|
+
outputs = list(MyNode().run())
|
462
|
+
|
463
|
+
# THEN the last output is as expected
|
464
|
+
assert outputs[-1].value == "Test"
|
465
|
+
|
466
|
+
# AND the prompt is executed with the correct execution context
|
467
|
+
call_request_args = mock_httpx_transport.handle_request.call_args_list[0][0][0]
|
468
|
+
request_execution_context = json.loads(call_request_args.read().decode("utf-8"))["execution_context"]
|
469
|
+
assert request_execution_context["trace_id"] == str(trace_id)
|
470
|
+
assert request_execution_context["parent_context"]
|
@@ -35,6 +35,9 @@ class WorkflowInputReference(BaseDescriptor[_InputType], Generic[_InputType]):
|
|
35
35
|
if state.meta.parent:
|
36
36
|
return self.resolve(state.meta.parent)
|
37
37
|
|
38
|
+
if type(None) in self.types:
|
39
|
+
return cast(_InputType, None)
|
40
|
+
|
38
41
|
raise NodeException(f"Missing required Workflow input: {self._name}", code=WorkflowErrorCode.INVALID_INPUTS)
|
39
42
|
|
40
43
|
def __repr__(self) -> str:
|
@@ -321,6 +321,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
321
321
|
)
|
322
322
|
)
|
323
323
|
except NodeException as e:
|
324
|
+
logger.info(e)
|
324
325
|
self._workflow_event_inner_queue.put(
|
325
326
|
NodeExecutionRejectedEvent(
|
326
327
|
trace_id=node.state.meta.trace_id,
|
@@ -333,6 +334,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
333
334
|
)
|
334
335
|
)
|
335
336
|
except WorkflowInitializationException as e:
|
337
|
+
logger.info(e)
|
336
338
|
self._workflow_event_inner_queue.put(
|
337
339
|
NodeExecutionRejectedEvent(
|
338
340
|
trace_id=node.state.meta.trace_id,
|