vellum-ai 1.3.3__py3-none-any.whl → 1.3.4__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/client/core/client_wrapper.py +2 -2
- vellum/client/types/function_definition.py +5 -0
- vellum/client/types/scenario_input_audio_variable_value.py +1 -1
- vellum/client/types/scenario_input_document_variable_value.py +1 -1
- vellum/client/types/scenario_input_image_variable_value.py +1 -1
- vellum/client/types/scenario_input_video_variable_value.py +1 -1
- vellum/workflows/events/node.py +1 -1
- vellum/workflows/events/tests/test_event.py +1 -1
- vellum/workflows/events/workflow.py +1 -1
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +15 -2
- vellum/workflows/resolvers/resolver.py +18 -2
- vellum/workflows/resolvers/tests/test_resolver.py +121 -0
- vellum/workflows/runner/runner.py +17 -17
- vellum/workflows/state/encoder.py +0 -37
- vellum/workflows/utils/functions.py +35 -0
- {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/METADATA +1 -1
- {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/RECORD +28 -28
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +18 -2
- vellum_ee/workflows/display/tests/test_base_workflow_display.py +52 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +1 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +1 -0
- vellum_ee/workflows/display/utils/events.py +1 -0
- vellum_ee/workflows/display/utils/expressions.py +44 -0
- vellum_ee/workflows/display/utils/tests/test_events.py +11 -1
- vellum_ee/workflows/display/workflows/base_workflow_display.py +32 -22
- {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/LICENSE +0 -0
- {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/WHEEL +0 -0
- {vellum_ai-1.3.3.dist-info → vellum_ai-1.3.4.dist-info}/entry_points.txt +0 -0
@@ -27,10 +27,10 @@ class BaseClientWrapper:
|
|
27
27
|
|
28
28
|
def get_headers(self) -> typing.Dict[str, str]:
|
29
29
|
headers: typing.Dict[str, str] = {
|
30
|
-
"User-Agent": "vellum-ai/1.3.
|
30
|
+
"User-Agent": "vellum-ai/1.3.4",
|
31
31
|
"X-Fern-Language": "Python",
|
32
32
|
"X-Fern-SDK-Name": "vellum-ai",
|
33
|
-
"X-Fern-SDK-Version": "1.3.
|
33
|
+
"X-Fern-SDK-Version": "1.3.4",
|
34
34
|
**(self.get_custom_headers() or {}),
|
35
35
|
}
|
36
36
|
if self._api_version is not None:
|
@@ -30,6 +30,11 @@ class FunctionDefinition(UniversalBaseModel):
|
|
30
30
|
An OpenAPI specification of parameters that are supported by this function.
|
31
31
|
"""
|
32
32
|
|
33
|
+
inputs: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None)
|
34
|
+
"""
|
35
|
+
Optional user defined input mappings for this function.
|
36
|
+
"""
|
37
|
+
|
33
38
|
forced: typing.Optional[bool] = pydantic.Field(default=None)
|
34
39
|
"""
|
35
40
|
Set this option to true to force the model to return a function call of this function.
|
@@ -9,7 +9,7 @@ from .vellum_audio import VellumAudio
|
|
9
9
|
|
10
10
|
class ScenarioInputAudioVariableValue(UniversalBaseModel):
|
11
11
|
type: typing.Literal["AUDIO"] = "AUDIO"
|
12
|
-
value: VellumAudio
|
12
|
+
value: typing.Optional[VellumAudio] = None
|
13
13
|
input_variable_id: str
|
14
14
|
|
15
15
|
if IS_PYDANTIC_V2:
|
@@ -9,7 +9,7 @@ from .vellum_document import VellumDocument
|
|
9
9
|
|
10
10
|
class ScenarioInputDocumentVariableValue(UniversalBaseModel):
|
11
11
|
type: typing.Literal["DOCUMENT"] = "DOCUMENT"
|
12
|
-
value: VellumDocument
|
12
|
+
value: typing.Optional[VellumDocument] = None
|
13
13
|
input_variable_id: str
|
14
14
|
|
15
15
|
if IS_PYDANTIC_V2:
|
@@ -9,7 +9,7 @@ from .vellum_image import VellumImage
|
|
9
9
|
|
10
10
|
class ScenarioInputImageVariableValue(UniversalBaseModel):
|
11
11
|
type: typing.Literal["IMAGE"] = "IMAGE"
|
12
|
-
value: VellumImage
|
12
|
+
value: typing.Optional[VellumImage] = None
|
13
13
|
input_variable_id: str
|
14
14
|
|
15
15
|
if IS_PYDANTIC_V2:
|
@@ -9,7 +9,7 @@ from .vellum_video import VellumVideo
|
|
9
9
|
|
10
10
|
class ScenarioInputVideoVariableValue(UniversalBaseModel):
|
11
11
|
type: typing.Literal["VIDEO"] = "VIDEO"
|
12
|
-
value: VellumVideo
|
12
|
+
value: typing.Optional[VellumVideo] = None
|
13
13
|
input_variable_id: str
|
14
14
|
|
15
15
|
if IS_PYDANTIC_V2:
|
vellum/workflows/events/node.py
CHANGED
@@ -141,7 +141,7 @@ class NodeExecutionFulfilledEvent(_BaseNodeEvent, Generic[OutputsType]):
|
|
141
141
|
|
142
142
|
class NodeExecutionRejectedBody(_BaseNodeExecutionBody):
|
143
143
|
error: WorkflowError
|
144
|
-
|
144
|
+
stacktrace: Optional[str] = None
|
145
145
|
|
146
146
|
|
147
147
|
class NodeExecutionRejectedEvent(_BaseNodeEvent):
|
@@ -156,7 +156,7 @@ class WorkflowExecutionFulfilledEvent(_BaseWorkflowEvent, Generic[OutputsType]):
|
|
156
156
|
|
157
157
|
class WorkflowExecutionRejectedBody(_BaseWorkflowExecutionBody):
|
158
158
|
error: WorkflowError
|
159
|
-
|
159
|
+
stacktrace: Optional[str] = None
|
160
160
|
|
161
161
|
|
162
162
|
class WorkflowExecutionRejectedEvent(_BaseWorkflowEvent):
|
@@ -520,12 +520,25 @@ def create_function_node(
|
|
520
520
|
},
|
521
521
|
)
|
522
522
|
else:
|
523
|
-
|
523
|
+
|
524
|
+
def create_function_wrapper(func):
|
525
|
+
def wrapper(self, **kwargs):
|
526
|
+
merged_kwargs = kwargs.copy()
|
527
|
+
inputs = getattr(func, "__vellum_inputs__", {})
|
528
|
+
if inputs:
|
529
|
+
for param_name, param_ref in inputs.items():
|
530
|
+
resolved_value = param_ref.resolve(self.state)
|
531
|
+
merged_kwargs[param_name] = resolved_value
|
532
|
+
|
533
|
+
return func(**merged_kwargs)
|
534
|
+
|
535
|
+
return wrapper
|
536
|
+
|
524
537
|
node = type(
|
525
538
|
f"FunctionNode_{function.__name__}",
|
526
539
|
(FunctionNode,),
|
527
540
|
{
|
528
|
-
"function_definition":
|
541
|
+
"function_definition": create_function_wrapper(function),
|
529
542
|
"function_call_output": tool_prompt_node.Outputs.results,
|
530
543
|
"__module__": __name__,
|
531
544
|
},
|
@@ -1,10 +1,11 @@
|
|
1
1
|
import logging
|
2
2
|
from uuid import UUID
|
3
|
-
from typing import Iterator, List, Optional, Tuple, Union
|
3
|
+
from typing import Iterator, List, Optional, Tuple, Type, Union
|
4
4
|
|
5
5
|
from vellum.client.types.vellum_span import VellumSpan
|
6
6
|
from vellum.client.types.workflow_execution_initiated_event import WorkflowExecutionInitiatedEvent
|
7
7
|
from vellum.workflows.events.workflow import WorkflowEvent
|
8
|
+
from vellum.workflows.nodes.utils import cast_to_output_type
|
8
9
|
from vellum.workflows.resolvers.base import BaseWorkflowResolver
|
9
10
|
from vellum.workflows.resolvers.types import LoadStateResult
|
10
11
|
from vellum.workflows.state.base import BaseState
|
@@ -51,6 +52,21 @@ class VellumResolver(BaseWorkflowResolver):
|
|
51
52
|
|
52
53
|
return previous_trace_id, root_trace_id, previous_span_id, root_span_id
|
53
54
|
|
55
|
+
def _deserialize_state(self, state_data: dict, state_class: Type[BaseState]) -> BaseState:
|
56
|
+
"""Deserialize state data with proper type conversion for complex types like List[ChatMessage]."""
|
57
|
+
converted_data = {}
|
58
|
+
|
59
|
+
annotations = getattr(state_class, "__annotations__", {})
|
60
|
+
|
61
|
+
for field_name, field_value in state_data.items():
|
62
|
+
if field_name in annotations:
|
63
|
+
field_type = annotations[field_name]
|
64
|
+
converted_data[field_name] = cast_to_output_type(field_value, field_type)
|
65
|
+
else:
|
66
|
+
converted_data[field_name] = field_value
|
67
|
+
|
68
|
+
return state_class(**converted_data)
|
69
|
+
|
54
70
|
def load_state(self, previous_execution_id: Optional[Union[UUID, str]] = None) -> Optional[LoadStateResult]:
|
55
71
|
if isinstance(previous_execution_id, UUID):
|
56
72
|
previous_execution_id = str(previous_execution_id)
|
@@ -83,7 +99,7 @@ class VellumResolver(BaseWorkflowResolver):
|
|
83
99
|
|
84
100
|
if self._workflow_class:
|
85
101
|
state_class = self._workflow_class.get_state_class()
|
86
|
-
state =
|
102
|
+
state = self._deserialize_state(response.state, state_class)
|
87
103
|
else:
|
88
104
|
logger.warning("No workflow class registered, falling back to BaseState")
|
89
105
|
state = BaseState(**response.state)
|
@@ -1,7 +1,9 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from unittest.mock import Mock
|
3
3
|
from uuid import uuid4
|
4
|
+
from typing import List
|
4
5
|
|
6
|
+
from vellum import ChatMessage
|
5
7
|
from vellum.client.types.span_link import SpanLink
|
6
8
|
from vellum.client.types.vellum_code_resource_definition import VellumCodeResourceDefinition
|
7
9
|
from vellum.client.types.workflow_execution_detail import WorkflowExecutionDetail
|
@@ -129,3 +131,122 @@ def test_load_state_with_context_success():
|
|
129
131
|
mock_client.workflow_executions.retrieve_workflow_execution_detail.assert_called_once_with(
|
130
132
|
execution_id=str(execution_id)
|
131
133
|
)
|
134
|
+
|
135
|
+
|
136
|
+
def test_load_state_with_chat_message_list():
|
137
|
+
"""Test load_state successfully loads state with chat_history containing ChatMessage list."""
|
138
|
+
resolver = VellumResolver()
|
139
|
+
execution_id = uuid4()
|
140
|
+
root_execution_id = uuid4()
|
141
|
+
|
142
|
+
class TestStateWithChatHistory(BaseState):
|
143
|
+
test_key: str = "test_value"
|
144
|
+
chat_history: List[ChatMessage] = []
|
145
|
+
|
146
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, TestStateWithChatHistory]):
|
147
|
+
pass
|
148
|
+
|
149
|
+
# GIVEN a state dictionary with chat_history containing ChatMessage objects
|
150
|
+
prev_id = str(uuid4())
|
151
|
+
prev_span_id = str(uuid4())
|
152
|
+
state_dict = {
|
153
|
+
"test_key": "test_value",
|
154
|
+
"chat_history": [
|
155
|
+
{"role": "USER", "text": "Hello, how are you?"},
|
156
|
+
{"role": "ASSISTANT", "text": "I'm doing well, thank you!"},
|
157
|
+
{"role": "USER", "text": "What can you help me with?"},
|
158
|
+
],
|
159
|
+
"meta": {
|
160
|
+
"workflow_definition": "MockWorkflow",
|
161
|
+
"id": prev_id,
|
162
|
+
"span_id": prev_span_id,
|
163
|
+
"updated_ts": datetime.now().isoformat(),
|
164
|
+
"workflow_inputs": BaseInputs(),
|
165
|
+
"external_inputs": {},
|
166
|
+
"node_outputs": {},
|
167
|
+
"node_execution_cache": NodeExecutionCache(),
|
168
|
+
"parent": None,
|
169
|
+
},
|
170
|
+
}
|
171
|
+
|
172
|
+
mock_workflow_definition = VellumCodeResourceDefinition(
|
173
|
+
name="TestWorkflow", module=["test", "module"], id=str(uuid4())
|
174
|
+
)
|
175
|
+
|
176
|
+
mock_body = WorkflowExecutionInitiatedBody(workflow_definition=mock_workflow_definition, inputs={})
|
177
|
+
|
178
|
+
previous_trace_id = str(uuid4())
|
179
|
+
root_trace_id = str(uuid4())
|
180
|
+
|
181
|
+
previous_invocation = WorkflowExecutionInitiatedEvent(
|
182
|
+
id=str(uuid4()),
|
183
|
+
timestamp=datetime.now(),
|
184
|
+
trace_id=previous_trace_id,
|
185
|
+
span_id=str(execution_id),
|
186
|
+
body=mock_body,
|
187
|
+
links=[
|
188
|
+
SpanLink(
|
189
|
+
trace_id=previous_trace_id,
|
190
|
+
type="PREVIOUS_SPAN",
|
191
|
+
span_context=WorkflowParentContext(workflow_definition=mock_workflow_definition, span_id=str(uuid4())),
|
192
|
+
),
|
193
|
+
SpanLink(
|
194
|
+
trace_id=root_trace_id,
|
195
|
+
type="ROOT_SPAN",
|
196
|
+
span_context=WorkflowParentContext(
|
197
|
+
workflow_definition=mock_workflow_definition, span_id=str(root_execution_id)
|
198
|
+
),
|
199
|
+
),
|
200
|
+
],
|
201
|
+
)
|
202
|
+
|
203
|
+
root_invocation = WorkflowExecutionInitiatedEvent(
|
204
|
+
id=str(uuid4()),
|
205
|
+
timestamp=datetime.now(),
|
206
|
+
trace_id=root_trace_id,
|
207
|
+
span_id=str(root_execution_id),
|
208
|
+
body=mock_body,
|
209
|
+
links=None,
|
210
|
+
)
|
211
|
+
|
212
|
+
mock_span = WorkflowExecutionSpan(
|
213
|
+
span_id=str(execution_id),
|
214
|
+
start_ts=datetime.now(),
|
215
|
+
end_ts=datetime.now(),
|
216
|
+
attributes=WorkflowExecutionSpanAttributes(label="Test Workflow", workflow_id=str(uuid4())),
|
217
|
+
events=[previous_invocation, root_invocation],
|
218
|
+
)
|
219
|
+
|
220
|
+
mock_response = WorkflowExecutionDetail(
|
221
|
+
span_id="test-span-id", start=datetime.now(), inputs=[], outputs=[], spans=[mock_span], state=state_dict
|
222
|
+
)
|
223
|
+
|
224
|
+
mock_client = Mock()
|
225
|
+
mock_client.workflow_executions.retrieve_workflow_execution_detail.return_value = mock_response
|
226
|
+
|
227
|
+
# AND context with the test workflow class is set up
|
228
|
+
context = WorkflowContext(vellum_client=mock_client)
|
229
|
+
TestWorkflow(context=context, resolvers=[resolver])
|
230
|
+
|
231
|
+
# WHEN load_state is called
|
232
|
+
result = resolver.load_state(previous_execution_id=execution_id)
|
233
|
+
|
234
|
+
# THEN should return LoadStateResult with state containing chat_history
|
235
|
+
assert isinstance(result, LoadStateResult)
|
236
|
+
assert result.state is not None
|
237
|
+
assert isinstance(result.state, TestStateWithChatHistory)
|
238
|
+
assert result.state.test_key == "test_value"
|
239
|
+
|
240
|
+
# AND the chat_history should be properly deserialized as ChatMessage objects
|
241
|
+
assert len(result.state.chat_history) == 3
|
242
|
+
assert all(isinstance(msg, ChatMessage) for msg in result.state.chat_history)
|
243
|
+
assert result.state.chat_history[0].role == "USER"
|
244
|
+
assert result.state.chat_history[0].text == "Hello, how are you?"
|
245
|
+
assert result.state.chat_history[1].role == "ASSISTANT"
|
246
|
+
assert result.state.chat_history[1].text == "I'm doing well, thank you!"
|
247
|
+
assert result.state.chat_history[2].role == "USER"
|
248
|
+
assert result.state.chat_history[2].text == "What can you help me with?"
|
249
|
+
|
250
|
+
mock_client.workflow_executions.retrieve_workflow_execution_detail.assert_called_once_with(
|
251
|
+
execution_id=str(execution_id)
|
252
|
+
)
|
@@ -404,7 +404,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
404
404
|
)
|
405
405
|
except NodeException as e:
|
406
406
|
logger.info(e)
|
407
|
-
|
407
|
+
captured_stacktrace = traceback.format_exc()
|
408
408
|
|
409
409
|
self._workflow_event_inner_queue.put(
|
410
410
|
NodeExecutionRejectedEvent(
|
@@ -413,14 +413,14 @@ class WorkflowRunner(Generic[StateType]):
|
|
413
413
|
body=NodeExecutionRejectedBody(
|
414
414
|
node_definition=node.__class__,
|
415
415
|
error=e.error,
|
416
|
-
|
416
|
+
stacktrace=captured_stacktrace,
|
417
417
|
),
|
418
418
|
parent=execution.parent_context,
|
419
419
|
)
|
420
420
|
)
|
421
421
|
except WorkflowInitializationException as e:
|
422
422
|
logger.info(e)
|
423
|
-
|
423
|
+
captured_stacktrace = traceback.format_exc()
|
424
424
|
self._workflow_event_inner_queue.put(
|
425
425
|
NodeExecutionRejectedEvent(
|
426
426
|
trace_id=execution.trace_id,
|
@@ -428,7 +428,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
428
428
|
body=NodeExecutionRejectedBody(
|
429
429
|
node_definition=node.__class__,
|
430
430
|
error=e.error,
|
431
|
-
|
431
|
+
stacktrace=captured_stacktrace,
|
432
432
|
),
|
433
433
|
parent=execution.parent_context,
|
434
434
|
)
|
@@ -713,13 +713,13 @@ class WorkflowRunner(Generic[StateType]):
|
|
713
713
|
)
|
714
714
|
|
715
715
|
def _reject_workflow_event(
|
716
|
-
self, error: WorkflowError,
|
716
|
+
self, error: WorkflowError, captured_stacktrace: Optional[str] = None
|
717
717
|
) -> WorkflowExecutionRejectedEvent:
|
718
|
-
if
|
718
|
+
if captured_stacktrace is None:
|
719
719
|
try:
|
720
|
-
|
721
|
-
if
|
722
|
-
|
720
|
+
captured_stacktrace = traceback.format_exc()
|
721
|
+
if captured_stacktrace.strip() == "NoneType: None":
|
722
|
+
captured_stacktrace = None
|
723
723
|
except Exception:
|
724
724
|
pass
|
725
725
|
|
@@ -729,7 +729,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
729
729
|
body=WorkflowExecutionRejectedBody(
|
730
730
|
workflow_definition=self.workflow.__class__,
|
731
731
|
error=error,
|
732
|
-
|
732
|
+
stacktrace=captured_stacktrace,
|
733
733
|
),
|
734
734
|
parent=self._execution_context.parent_context,
|
735
735
|
)
|
@@ -773,21 +773,21 @@ class WorkflowRunner(Generic[StateType]):
|
|
773
773
|
else:
|
774
774
|
self._concurrency_queue.put((self._initial_state, node_cls, None))
|
775
775
|
except NodeException as e:
|
776
|
-
|
777
|
-
self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error,
|
776
|
+
captured_stacktrace = traceback.format_exc()
|
777
|
+
self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error, captured_stacktrace))
|
778
778
|
return
|
779
779
|
except WorkflowInitializationException as e:
|
780
|
-
|
781
|
-
self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error,
|
780
|
+
captured_stacktrace = traceback.format_exc()
|
781
|
+
self._workflow_event_outer_queue.put(self._reject_workflow_event(e.error, captured_stacktrace))
|
782
782
|
return
|
783
783
|
except Exception:
|
784
784
|
err_message = f"An unexpected error occurred while initializing node {node_cls.__name__}"
|
785
785
|
logger.exception(err_message)
|
786
|
-
|
786
|
+
captured_stacktrace = traceback.format_exc()
|
787
787
|
self._workflow_event_outer_queue.put(
|
788
788
|
self._reject_workflow_event(
|
789
789
|
WorkflowError(code=WorkflowErrorCode.INTERNAL_ERROR, message=err_message),
|
790
|
-
|
790
|
+
captured_stacktrace,
|
791
791
|
)
|
792
792
|
)
|
793
793
|
return
|
@@ -838,7 +838,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
838
838
|
|
839
839
|
if rejection_event:
|
840
840
|
self._workflow_event_outer_queue.put(
|
841
|
-
self._reject_workflow_event(rejection_event.error, rejection_event.body.
|
841
|
+
self._reject_workflow_event(rejection_event.error, rejection_event.body.stacktrace)
|
842
842
|
)
|
843
843
|
return
|
844
844
|
|
@@ -1,11 +1,8 @@
|
|
1
1
|
from dataclasses import asdict, is_dataclass
|
2
2
|
from datetime import datetime
|
3
3
|
import enum
|
4
|
-
import inspect
|
5
|
-
from io import StringIO
|
6
4
|
from json import JSONEncoder
|
7
5
|
from queue import Queue
|
8
|
-
import sys
|
9
6
|
from uuid import UUID
|
10
7
|
from typing import Any, Callable, Dict, Type
|
11
8
|
|
@@ -17,23 +14,6 @@ from vellum.workflows.inputs.base import BaseInputs
|
|
17
14
|
from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
|
18
15
|
from vellum.workflows.ports.port import Port
|
19
16
|
from vellum.workflows.state.base import BaseState, NodeExecutionCache
|
20
|
-
from vellum.workflows.utils.functions import compile_function_definition
|
21
|
-
|
22
|
-
|
23
|
-
def virtual_open(file_path: str, mode: str = "r"):
|
24
|
-
"""
|
25
|
-
Open a file, checking VirtualFileFinder instances first before falling back to regular open().
|
26
|
-
"""
|
27
|
-
for finder in sys.meta_path:
|
28
|
-
if hasattr(finder, "loader") and hasattr(finder.loader, "_get_code"):
|
29
|
-
namespace = finder.loader.namespace
|
30
|
-
if file_path.startswith(namespace + "/"):
|
31
|
-
relative_path = file_path[len(namespace) + 1 :]
|
32
|
-
content = finder.loader._get_code(relative_path)
|
33
|
-
if content is not None:
|
34
|
-
return StringIO(content)
|
35
|
-
|
36
|
-
return open(file_path, mode)
|
37
17
|
|
38
18
|
|
39
19
|
class DefaultStateEncoder(JSONEncoder):
|
@@ -80,23 +60,6 @@ class DefaultStateEncoder(JSONEncoder):
|
|
80
60
|
if isinstance(obj, type):
|
81
61
|
return str(obj)
|
82
62
|
|
83
|
-
if callable(obj):
|
84
|
-
function_definition = compile_function_definition(obj)
|
85
|
-
source_path = inspect.getsourcefile(obj)
|
86
|
-
if source_path is not None:
|
87
|
-
with virtual_open(source_path) as f:
|
88
|
-
source_code = f.read()
|
89
|
-
else:
|
90
|
-
source_code = f"# Error: Source code not available for {obj.__name__}"
|
91
|
-
|
92
|
-
return {
|
93
|
-
"type": "CODE_EXECUTION",
|
94
|
-
"name": function_definition.name,
|
95
|
-
"description": function_definition.description,
|
96
|
-
"definition": function_definition,
|
97
|
-
"src": source_code,
|
98
|
-
}
|
99
|
-
|
100
63
|
if obj.__class__ in self.encoders:
|
101
64
|
return self.encoders[obj.__class__](obj)
|
102
65
|
|
@@ -135,6 +135,9 @@ def _compile_workflow_deployment_input(input_var: Any) -> dict[str, Any]:
|
|
135
135
|
def compile_function_definition(function: Callable) -> FunctionDefinition:
|
136
136
|
"""
|
137
137
|
Converts a Python function into our Vellum-native FunctionDefinition type.
|
138
|
+
|
139
|
+
Args:
|
140
|
+
function: The Python function to compile
|
138
141
|
"""
|
139
142
|
|
140
143
|
try:
|
@@ -142,10 +145,18 @@ def compile_function_definition(function: Callable) -> FunctionDefinition:
|
|
142
145
|
except ValueError as e:
|
143
146
|
raise ValueError(f"Failed to get signature for function {function.__name__}: {str(e)}")
|
144
147
|
|
148
|
+
# Get inputs from the decorator if present
|
149
|
+
inputs = getattr(function, "__vellum_inputs__", {})
|
150
|
+
exclude_params = set(inputs.keys())
|
151
|
+
|
145
152
|
properties = {}
|
146
153
|
required = []
|
147
154
|
defs: dict[str, Any] = {}
|
148
155
|
for param in signature.parameters.values():
|
156
|
+
# Skip parameters that are in the exclude_params set
|
157
|
+
if exclude_params and param.name in exclude_params:
|
158
|
+
continue
|
159
|
+
|
149
160
|
# Check if parameter uses Annotated type hint
|
150
161
|
if get_origin(param.annotation) is Annotated:
|
151
162
|
args = get_args(param.annotation)
|
@@ -248,3 +259,27 @@ def compile_workflow_deployment_function_definition(
|
|
248
259
|
description=description,
|
249
260
|
parameters=parameters,
|
250
261
|
)
|
262
|
+
|
263
|
+
|
264
|
+
def use_tool_inputs(**inputs):
|
265
|
+
"""
|
266
|
+
Decorator to specify which parameters of a tool function should be provided
|
267
|
+
from the parent workflow inputs rather than from the LLM.
|
268
|
+
|
269
|
+
Args:
|
270
|
+
**inputs: Mapping of parameter names to parent input references
|
271
|
+
|
272
|
+
Example:
|
273
|
+
@use_tool_inputs(
|
274
|
+
parent_input=ParentInputs.parent_input,
|
275
|
+
)
|
276
|
+
def get_string(parent_input: str, user_query: str) -> str:
|
277
|
+
return f"Parent: {parent_input}, Query: {user_query}"
|
278
|
+
"""
|
279
|
+
|
280
|
+
def decorator(func: Callable) -> Callable:
|
281
|
+
# Store the inputs mapping on the function for later use
|
282
|
+
setattr(func, "__vellum_inputs__", inputs)
|
283
|
+
return func
|
284
|
+
|
285
|
+
return decorator
|
@@ -39,7 +39,7 @@ vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqU
|
|
39
39
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
|
40
40
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=TD23uXA4vhRvsfiq7B10XHpFnsS1CA9M7WH9jsOYNB0,9242
|
41
41
|
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=FHhPoGmmny4Xcxi2pm12Sk3ZOREanWEVrOWcjRhncH4,6337
|
42
|
-
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=
|
42
|
+
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=GfwA2Tn0OpdIUiPW9tD2dyv33e3M8yhGt9VfOeN81bU,5146
|
43
43
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=dtO9A-rjbDEJrywwrOxwEXahqrW-S493OIDHOti9Sjs,11498
|
44
44
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=8tSb8qGVoRIELubu0qPeoDlt1LpiIqc6d9_30GWRd_k,2266
|
45
45
|
vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=eeu8i01HbLDvO3KwHnAfaMrYKiEchNAdDdjufjCi6OU,2991
|
@@ -72,7 +72,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=rHybfUAWwa0L
|
|
72
72
|
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=47fOnSCEFnY8th9m2yTYlgnoUuzgyRZdjg-SXwn--lk,4079
|
73
73
|
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=oICunzyaXPs0tYnW5zH1r93Bx35MSH7mcD-n0DEWRok,4978
|
74
74
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
|
-
vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=
|
75
|
+
vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=KUxFl04uZv1xPfnLHsMOjPKygwJkf6fRvPj0JvV8Des,14190
|
76
76
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
77
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
78
78
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=Y-ajeT65b5varmrZCw6L3hir4hJCFq-eO0jZfRcrs7g,1886
|
@@ -88,7 +88,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_stat
|
|
88
88
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=03Mk8OE3kWcoZW9lNBe7v4KThCYN-pXg5Rjbkfx-jOQ,6031
|
89
89
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=-T0cd2jx1bC0ZNtAESF78fnYD_0nOqo8zMMLwRHUTRM,6227
|
90
90
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=LnZp1YXDn-AaaxiYgxrhCQeH-rLgmlu_r_lvM65EQ5w,7517
|
91
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=
|
91
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=9w90pk1DzX2PDRML401bZpD9hdTW53Dw5oGb58mxpiw,26358
|
92
92
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=yKmOyunzt5_0cUcqhvCmV2pu81TTkshVi_uN3yt76Wc,21816
|
93
93
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=W4--Ldih7mRMnfyJ9G7kdyeoKkeebSu_5d33TJQzShU,16735
|
94
94
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=UnfWTfKN8DrOLpjCfWMgENJBjgNLWcRVfexbwERKY-c,8501
|
@@ -100,7 +100,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_nod
|
|
100
100
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=oVXCjkU0G56QJmqnd_xIwF3D9bhJwALFibM2wmRhwUk,3739
|
101
101
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=Sg82qV5NCzQDy-RD90hA6QaHgFHOq6ESNkbWXygsnNw,26367
|
102
102
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py,sha256=QhQbijeCnFeX1i3SMjHJg2WVAEt5JEO3dhFRv-mofdA,2458
|
103
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256
|
103
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=cvjHGrgN1LbRvuSKq2U7eLrRFDK4Rb6VJt8L2HIC8HY,10137
|
104
104
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=XIZZr5POo2NLn2uEWm9EC3rejeBMoO4X-JtzTH6mvp4,4074
|
105
105
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
|
106
106
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=exT7U-axwtYgFylagScflSQLJEND51qIAx2UATju6JM,6023
|
@@ -109,17 +109,17 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_par
|
|
109
109
|
vellum_ee/workflows/display/types.py,sha256=cyZruu4sXAdHjwuFc7dydM4DcFNf-pp_CmulXItxac4,3679
|
110
110
|
vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
111
111
|
vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
|
112
|
-
vellum_ee/workflows/display/utils/events.py,sha256=
|
112
|
+
vellum_ee/workflows/display/utils/events.py,sha256=MEG2BT6GgAzkbv1dMaFpov5OShtaAZeAb1-g3xDFsAM,1826
|
113
113
|
vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
|
114
|
-
vellum_ee/workflows/display/utils/expressions.py,sha256=
|
114
|
+
vellum_ee/workflows/display/utils/expressions.py,sha256=5DwluPppoJSsMf0pfbRafPIisZHZ4VFueXR1YeLcRwY,18593
|
115
115
|
vellum_ee/workflows/display/utils/registry.py,sha256=1qXiBTdsnro6FeCX0FGBEK7CIf6wa--Jt50iZ_nEp_M,3460
|
116
116
|
vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
117
|
vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
|
118
|
-
vellum_ee/workflows/display/utils/tests/test_events.py,sha256=
|
118
|
+
vellum_ee/workflows/display/utils/tests/test_events.py,sha256=Qze6wEmFJx23_sKQhX-i329apWgMeS9zTptWlRca6Ko,4528
|
119
119
|
vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
|
120
120
|
vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
|
121
121
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
|
122
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
122
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=o1yKDaBv3dECPcJnEtjDZw8Nt9Y5kjijGc7FQi5IhOQ,43745
|
123
123
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
|
124
124
|
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=pb7BTH-ivRnya1LQU3j-MApWk_m8POpPNOdD0oEK82A,37847
|
125
125
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -153,7 +153,7 @@ vellum/client/README.md,sha256=b6XKeYBBbhQx0v1sHWfM0gIJeJhUFF-aqL2ig7ADa08,5564
|
|
153
153
|
vellum/client/__init__.py,sha256=T5Ht_w-Mk_9nzGqdadhQB8V20M0vYj7am06ut0A3P1o,73401
|
154
154
|
vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
|
155
155
|
vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
|
156
|
-
vellum/client/core/client_wrapper.py,sha256=
|
156
|
+
vellum/client/core/client_wrapper.py,sha256=7aJQmDvTC5-vD5Qkr7IPOm7wV5Tj_nzD5SlG1cAUg9g,2840
|
157
157
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
158
158
|
vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
|
159
159
|
vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
|
@@ -420,7 +420,7 @@ vellum/client/types/function_call_prompt_block.py,sha256=YcuS0ndY-avXWIcG5UE9mNa
|
|
420
420
|
vellum/client/types/function_call_request.py,sha256=5nPW3gx-l9UN-9puJuDaEZKIkUJUd0glcRLaF0j1x_k,684
|
421
421
|
vellum/client/types/function_call_vellum_value.py,sha256=ITXP69WEE3yELyDylbpmLgIxnh4FWdNI14KkIX8boKA,723
|
422
422
|
vellum/client/types/function_call_vellum_value_request.py,sha256=9Xz4AZDQhtrUCICXdlq0IS9hpFr0bFHE6haf3g3Bn9c,752
|
423
|
-
vellum/client/types/function_definition.py,sha256=
|
423
|
+
vellum/client/types/function_definition.py,sha256=G4mQtdrJzC3I9984YfiyNmvozppEyWqt66NdZBqrgKc,1840
|
424
424
|
vellum/client/types/generate_options_request.py,sha256=2ZXU_zNraXRqcUwky8GqDAnhR10qb2NmcrIUaBFeF6w,746
|
425
425
|
vellum/client/types/generate_request.py,sha256=R_l2CmLeEF-GVIZHyV93Cbt6BBh2WklTslsyNbI3Vfo,1250
|
426
426
|
vellum/client/types/generate_response.py,sha256=_7F1PqE1aQLIBYqKLgw3UDQx7etR15sZgW-VzHfaaWA,1099
|
@@ -635,13 +635,13 @@ vellum/client/types/rich_text_child_block.py,sha256=JCC-6FLdxHG--gE-L6lJtRNI0pmn
|
|
635
635
|
vellum/client/types/rich_text_prompt_block.py,sha256=Te2qnJOiCgqV0gaiui8mVjnATX7mFNJjHqXDX4fd5jA,1000
|
636
636
|
vellum/client/types/sandbox_scenario.py,sha256=gaHbNzufZhibRGoo42Ryo6xZNRo30lLU39oSDS6mqIs,808
|
637
637
|
vellum/client/types/scenario_input.py,sha256=BxKRtG7u9boXbavG0kD-_-pFVk19LPzNFPZyfw64Lfo,967
|
638
|
-
vellum/client/types/scenario_input_audio_variable_value.py,sha256=
|
638
|
+
vellum/client/types/scenario_input_audio_variable_value.py,sha256=YsJ9vgdZtZ2KJkB-zK6xSQpg4LzRahQMdnIQVnqLTUw,680
|
639
639
|
vellum/client/types/scenario_input_chat_history_variable_value.py,sha256=FzXccgtyH9iTyGJJ6XcOM0TmzCMaPKWr1FmGtFCvxsQ,799
|
640
|
-
vellum/client/types/scenario_input_document_variable_value.py,sha256=
|
641
|
-
vellum/client/types/scenario_input_image_variable_value.py,sha256=
|
640
|
+
vellum/client/types/scenario_input_document_variable_value.py,sha256=xg-OOAbs7uegG9_O5fV3WhcUsM8hGXFeVL88-FJovec,698
|
641
|
+
vellum/client/types/scenario_input_image_variable_value.py,sha256=o3rwhVnXp3XCOKYSgLRzUXDo7k9WVIvqRCgL1hjyO18,680
|
642
642
|
vellum/client/types/scenario_input_json_variable_value.py,sha256=07_Fj9UOzMGvz1H0j1awmMz0Q6ErliwjgKVU_iUyZb8,716
|
643
643
|
vellum/client/types/scenario_input_string_variable_value.py,sha256=PI_pk-WOXVRBSW6PZKDZW11svxd2r9E6bCpl0zC0MTM,717
|
644
|
-
vellum/client/types/scenario_input_video_variable_value.py,sha256=
|
644
|
+
vellum/client/types/scenario_input_video_variable_value.py,sha256=6AxOsLtHNZv-1UyBue6fMs5_vLFCXidx7e_9kP0hHrs,680
|
645
645
|
vellum/client/types/search_filters_request.py,sha256=Moxewgl8KwjIy-VBistrchXQAqfsn5fkqO5R2UzLlI8,1276
|
646
646
|
vellum/client/types/search_node_result.py,sha256=2DbnKCus81-fj56IoGZqIL6Tw6hjf5jH0O2P_AXah8o,713
|
647
647
|
vellum/client/types/search_node_result_data.py,sha256=rXs0R0OG2pjTcE6bMp5lN6_cUusFiVUDwkC3pVo5l6o,810
|
@@ -1729,14 +1729,14 @@ vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy
|
|
1729
1729
|
vellum/workflows/errors/types.py,sha256=nUWuniEfrhdtb-_2GzoDGlYnSJ_yuNUGjVkaKLNr-rM,4049
|
1730
1730
|
vellum/workflows/events/__init__.py,sha256=V4mh766fyA70WvHelm9kfVZGrUgEKcJ9tJt8EepfQYU,832
|
1731
1731
|
vellum/workflows/events/context.py,sha256=vCfMIPmz4j9Om36rRWa35A_JU_VccWWS52_mZkkqxak,3345
|
1732
|
-
vellum/workflows/events/node.py,sha256=
|
1732
|
+
vellum/workflows/events/node.py,sha256=yHVd-rX2E3qc2XLnZr0fW6uq4ZCMm34mnY2tzYceyOg,5884
|
1733
1733
|
vellum/workflows/events/relational_threads.py,sha256=zmLrBCBYpdpQV0snKH3HleST-_hWAMy2LIT0xScfzi4,1516
|
1734
1734
|
vellum/workflows/events/stream.py,sha256=xhXJTZirFi0xad5neAQNogrIQ4h47fpnKbVC3vCM5Js,889
|
1735
1735
|
vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1736
1736
|
vellum/workflows/events/tests/test_basic_workflow.py,sha256=Pj6orHsXz37jWC5FARi0Sx2Gjf99Owri2Cvr6Chb79k,1765
|
1737
|
-
vellum/workflows/events/tests/test_event.py,sha256=
|
1737
|
+
vellum/workflows/events/tests/test_event.py,sha256=V26TNmbo2aL4sDvcY3nPxCWgcoS4ejF-7VuesMeD04U,18451
|
1738
1738
|
vellum/workflows/events/types.py,sha256=mVrqAH9Hs9SpXm08Hcxdyap_ImQPwGsxRr56rSNMP34,5043
|
1739
|
-
vellum/workflows/events/workflow.py,sha256=
|
1739
|
+
vellum/workflows/events/workflow.py,sha256=kLSWFXiDZH0TELWoDjQ_kHVomFnw8MVVUPDGIzgyosw,8997
|
1740
1740
|
vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
|
1741
1741
|
vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1742
1742
|
vellum/workflows/expressions/accessor.py,sha256=3lu1-_-dBfZdJvtX-q66jbmRAZtqIfdsh_3_JNuzg1E,4462
|
@@ -1897,7 +1897,7 @@ vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47
|
|
1897
1897
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=in1fbEz5x1tx3uKv9YXdvOncsHucNL8Ro6Go7lBuuOQ,8962
|
1898
1898
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=GZoeybB9uM7ai8sBLAtUMHrMVgh-WrJDWrKZci6feDs,11892
|
1899
1899
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=SIu5GCj4tIE4fz-cAcdULtQfqZIhrcc3Doo6TWLXBws,8804
|
1900
|
-
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=
|
1900
|
+
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=Y2MlODE0lRQ4Fci-G3eYZBpI7CQYe5cIykTLcF4jrbE,23602
|
1901
1901
|
vellum/workflows/nodes/displayable/web_search_node/__init__.py,sha256=8FOnEP-n-U68cvxTlJW9wphIAGHq5aqjzLM-DoSSXnU,61
|
1902
1902
|
vellum/workflows/nodes/displayable/web_search_node/node.py,sha256=NQYux2bOtuBF5E4tn-fXi5y3btURPRrNqMSM9MAZYI4,5091
|
1903
1903
|
vellum/workflows/nodes/displayable/web_search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1932,17 +1932,17 @@ vellum/workflows/references/vellum_secret.py,sha256=Od4d19a5yletWMqNfJR5d_mZQUkV
|
|
1932
1932
|
vellum/workflows/references/workflow_input.py,sha256=W3rOK1EPd2gYHb04WJwmNm1CUSdvZ9LKrs8RMKxACBs,1751
|
1933
1933
|
vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
|
1934
1934
|
vellum/workflows/resolvers/base.py,sha256=wrQiSC02Bw4-dBwgFjJIHsjpe-4xz4rUJs_1RdErKA0,1164
|
1935
|
-
vellum/workflows/resolvers/resolver.py,sha256=
|
1936
|
-
vellum/workflows/resolvers/tests/test_resolver.py,sha256=
|
1935
|
+
vellum/workflows/resolvers/resolver.py,sha256=yK-oY0HDsFJcjlNKAm3vpsPKRIFerIh59FcTwuEN5RY,4839
|
1936
|
+
vellum/workflows/resolvers/tests/test_resolver.py,sha256=jXkJBb9SwtoH__bBN-ECohpyD0aTIB9ErEvtFhuTMQM,9750
|
1937
1937
|
vellum/workflows/resolvers/types.py,sha256=Hndhlk69g6EKLh_LYg5ILepW5U_h_BYNllfzhS9k8p4,237
|
1938
1938
|
vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
|
1939
|
-
vellum/workflows/runner/runner.py,sha256=
|
1939
|
+
vellum/workflows/runner/runner.py,sha256=UJPCSddeczBcGu06j63KGM2IbbQ5xISTOwP-tFDptaI,40341
|
1940
1940
|
vellum/workflows/sandbox.py,sha256=jwlFFQjHDwmbVoBah_Q3i8K_BrzOt-F6TXFauiyVyIk,3021
|
1941
1941
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
1942
1942
|
vellum/workflows/state/base.py,sha256=m9fCqbZn21GshCVCjJTD1dPZEQjFrsMXqlg7tM9fIwM,24283
|
1943
1943
|
vellum/workflows/state/context.py,sha256=khM30U1iDNts5Xp8LXa_WfpkITNITexrDUUFJ5wZ2W4,8445
|
1944
1944
|
vellum/workflows/state/delta.py,sha256=7h8wR10lRCm15SykaPj-gSEvvsMjCwYLPsOx3nsvBQg,440
|
1945
|
-
vellum/workflows/state/encoder.py,sha256=
|
1945
|
+
vellum/workflows/state/encoder.py,sha256=EynuS9aCt9Neb-H6HRCinEVZX5olCzME03W1TSXfpxs,1961
|
1946
1946
|
vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
|
1947
1947
|
vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1948
1948
|
vellum/workflows/state/tests/test_state.py,sha256=zEVFIY2any41X2BA5Us_qqKpzH5HRqmyrUJ04GTO0pU,7484
|
@@ -1960,7 +1960,7 @@ vellum/workflows/types/tests/test_definition.py,sha256=rvDYjdJ1rvAv0qHBN7i7s-_WA
|
|
1960
1960
|
vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
|
1961
1961
|
vellum/workflows/types/utils.py,sha256=mTctHITBybpt4855x32oCKALBEcMNLn-9cCmfEKgJHQ,6498
|
1962
1962
|
vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1963
|
-
vellum/workflows/utils/functions.py,sha256=
|
1963
|
+
vellum/workflows/utils/functions.py,sha256=6WRRMb_XbxtvhUKOJq5ZChy0KKvlBaQCBiPhvecXT7I,10029
|
1964
1964
|
vellum/workflows/utils/hmac.py,sha256=JJCczc6pyV6DuE1Oa0QVfYPUN_of3zEYmGFib3OZnrE,1135
|
1965
1965
|
vellum/workflows/utils/names.py,sha256=QLUqfJ1tmSEeUwBKTTiv_Qk3QGbInC2RSmlXfGXc8Wo,380
|
1966
1966
|
vellum/workflows/utils/pydantic_schema.py,sha256=eR_bBtY-T0pttJP-ARwagSdCOnwPUtiT3cegm2lzDTQ,1310
|
@@ -1979,8 +1979,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1979
1979
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1980
1980
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=ptMntHzVyy8ZuzNgeTuk7hREgKQ5UBdgq8VJFSGaW4Y,20832
|
1981
1981
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1982
|
-
vellum_ai-1.3.
|
1983
|
-
vellum_ai-1.3.
|
1984
|
-
vellum_ai-1.3.
|
1985
|
-
vellum_ai-1.3.
|
1986
|
-
vellum_ai-1.3.
|
1982
|
+
vellum_ai-1.3.4.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1983
|
+
vellum_ai-1.3.4.dist-info/METADATA,sha256=b4vvPio8YVqk9c6RUtdneolPn9VxgV12Es1eAIxG0qM,5547
|
1984
|
+
vellum_ai-1.3.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1985
|
+
vellum_ai-1.3.4.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1986
|
+
vellum_ai-1.3.4.dist-info/RECORD,,
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import inspect
|
2
|
+
import os
|
2
3
|
from uuid import UUID
|
3
4
|
from typing import ClassVar, Generic, Optional, TypeVar
|
4
5
|
|
5
6
|
from vellum.workflows.nodes.displayable.code_execution_node import CodeExecutionNode
|
6
|
-
from vellum.workflows.nodes.displayable.code_execution_node.utils import read_file_from_path
|
7
7
|
from vellum.workflows.types.core import JsonObject
|
8
8
|
from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
|
9
9
|
from vellum_ee.workflows.display.exceptions import NodeValidationError
|
@@ -11,10 +11,26 @@ from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
|
11
11
|
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
12
12
|
from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
|
13
13
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
14
|
+
from vellum_ee.workflows.display.utils.expressions import virtual_open
|
14
15
|
|
15
16
|
_CodeExecutionNodeType = TypeVar("_CodeExecutionNodeType", bound=CodeExecutionNode)
|
16
17
|
|
17
18
|
|
19
|
+
def _read_file_from_path_with_virtual_support(node_filepath: str, script_filepath: str) -> Optional[str]:
|
20
|
+
"""
|
21
|
+
Read a file using virtual_open which handles VirtualFileFinder instances.
|
22
|
+
"""
|
23
|
+
node_filepath_dir = os.path.dirname(node_filepath)
|
24
|
+
normalized_script_filepath = script_filepath.lstrip("./")
|
25
|
+
full_filepath = os.path.join(node_filepath_dir, normalized_script_filepath)
|
26
|
+
|
27
|
+
try:
|
28
|
+
with virtual_open(full_filepath, "r") as file:
|
29
|
+
return file.read()
|
30
|
+
except (FileNotFoundError, IsADirectoryError):
|
31
|
+
return None
|
32
|
+
|
33
|
+
|
18
34
|
class BaseCodeExecutionNodeDisplay(BaseNodeDisplay[_CodeExecutionNodeType], Generic[_CodeExecutionNodeType]):
|
19
35
|
output_id: ClassVar[Optional[UUID]] = None
|
20
36
|
log_output_id: ClassVar[Optional[UUID]] = None
|
@@ -37,7 +53,7 @@ class BaseCodeExecutionNodeDisplay(BaseNodeDisplay[_CodeExecutionNodeType], Gene
|
|
37
53
|
code_value = raw_code
|
38
54
|
elif filepath:
|
39
55
|
node_file_path = inspect.getfile(node)
|
40
|
-
file_code =
|
56
|
+
file_code = _read_file_from_path_with_virtual_support(
|
41
57
|
node_filepath=node_file_path,
|
42
58
|
script_filepath=filepath,
|
43
59
|
)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import Dict
|
2
|
+
from typing import Any, Dict, List, cast
|
3
3
|
|
4
4
|
from vellum.workflows.inputs import BaseInputs
|
5
5
|
from vellum.workflows.nodes import BaseNode, InlineSubworkflowNode
|
@@ -8,7 +8,7 @@ from vellum.workflows.ports.port import Port
|
|
8
8
|
from vellum.workflows.references.lazy import LazyReference
|
9
9
|
from vellum.workflows.state import BaseState
|
10
10
|
from vellum.workflows.workflows.base import BaseWorkflow
|
11
|
-
from vellum_ee.workflows.display.base import WorkflowInputsDisplay
|
11
|
+
from vellum_ee.workflows.display.base import EdgeDisplay, WorkflowInputsDisplay
|
12
12
|
from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
|
13
13
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
14
14
|
|
@@ -379,3 +379,53 @@ def test_global_propagation_deep_nested_subworkflows():
|
|
379
379
|
inner_global_names = {ref.name for ref in inner_display.display_context.global_workflow_input_displays.keys()}
|
380
380
|
|
381
381
|
assert inner_global_names == {"middle_param", "inner_param", "root_param"}
|
382
|
+
|
383
|
+
|
384
|
+
def test_serialize_workflow_with_edge_display_data():
|
385
|
+
"""
|
386
|
+
Tests that edges with z_index values serialize display_data correctly.
|
387
|
+
"""
|
388
|
+
|
389
|
+
# GIVEN a workflow with connected nodes
|
390
|
+
class StartNode(BaseNode):
|
391
|
+
class Outputs(BaseNode.Outputs):
|
392
|
+
result: str
|
393
|
+
|
394
|
+
class EndNode(BaseNode):
|
395
|
+
class Outputs(BaseNode.Outputs):
|
396
|
+
final: str
|
397
|
+
|
398
|
+
class TestWorkflow(BaseWorkflow):
|
399
|
+
graph = StartNode >> EndNode
|
400
|
+
|
401
|
+
class Outputs(BaseWorkflow.Outputs):
|
402
|
+
final_result = EndNode.Outputs.final
|
403
|
+
|
404
|
+
class TestWorkflowDisplay(BaseWorkflowDisplay[TestWorkflow]):
|
405
|
+
edge_displays = {
|
406
|
+
(StartNode.Ports.default, EndNode): EdgeDisplay(id=UUID("12345678-1234-5678-1234-567812345678"), z_index=5)
|
407
|
+
}
|
408
|
+
|
409
|
+
# WHEN we serialize the workflow with the custom display
|
410
|
+
display = get_workflow_display(
|
411
|
+
base_display_class=TestWorkflowDisplay,
|
412
|
+
workflow_class=TestWorkflow,
|
413
|
+
)
|
414
|
+
serialized_workflow = display.serialize()
|
415
|
+
|
416
|
+
# THEN the edge should include display_data with z_index
|
417
|
+
workflow_raw_data = cast(Dict[str, Any], serialized_workflow["workflow_raw_data"])
|
418
|
+
edges = cast(List[Dict[str, Any]], workflow_raw_data["edges"])
|
419
|
+
|
420
|
+
edge_with_display_data = None
|
421
|
+
for edge in edges:
|
422
|
+
if edge["id"] == "12345678-1234-5678-1234-567812345678":
|
423
|
+
edge_with_display_data = edge
|
424
|
+
break
|
425
|
+
|
426
|
+
assert edge_with_display_data is not None, "Edge with custom UUID not found"
|
427
|
+
assert edge_with_display_data["display_data"] == {"z_index": 5}
|
428
|
+
|
429
|
+
assert edge_with_display_data["type"] == "DEFAULT"
|
430
|
+
assert "source_node_id" in edge_with_display_data
|
431
|
+
assert "target_node_id" in edge_with_display_data
|
@@ -36,6 +36,7 @@ def event_enricher(event: WorkflowExecutionInitiatedEvent) -> WorkflowExecutionI
|
|
36
36
|
dry_run=True,
|
37
37
|
)
|
38
38
|
register_workflow_display_context(event.span_id, workflow_display.display_context)
|
39
|
+
event.body.display_context = workflow_display.get_event_display_context()
|
39
40
|
|
40
41
|
if event.body.workflow_definition.is_dynamic or _should_mark_workflow_dynamic(event):
|
41
42
|
register_workflow_display_class(workflow_definition, workflow_display.__class__)
|
@@ -1,4 +1,7 @@
|
|
1
1
|
from dataclasses import asdict, is_dataclass
|
2
|
+
import inspect
|
3
|
+
from io import StringIO
|
4
|
+
import sys
|
2
5
|
from typing import TYPE_CHECKING, Any, Dict, List, cast
|
3
6
|
|
4
7
|
from pydantic import BaseModel
|
@@ -48,13 +51,31 @@ from vellum.workflows.references.workflow_input import WorkflowInputReference
|
|
48
51
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
49
52
|
from vellum.workflows.types.definition import DeploymentDefinition
|
50
53
|
from vellum.workflows.types.generics import is_workflow_class
|
54
|
+
from vellum.workflows.utils.functions import compile_function_definition
|
51
55
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
52
56
|
from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
|
57
|
+
from vellum_ee.workflows.server.virtual_file_loader import VirtualFileLoader
|
53
58
|
|
54
59
|
if TYPE_CHECKING:
|
55
60
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
56
61
|
|
57
62
|
|
63
|
+
def virtual_open(file_path: str, mode: str = "r"):
|
64
|
+
"""
|
65
|
+
Open a file, checking VirtualFileFinder instances first before falling back to regular open().
|
66
|
+
"""
|
67
|
+
for finder in sys.meta_path:
|
68
|
+
if hasattr(finder, "loader") and isinstance(finder.loader, VirtualFileLoader):
|
69
|
+
namespace = finder.loader.namespace
|
70
|
+
if file_path.startswith(namespace + "/"):
|
71
|
+
relative_path = file_path[len(namespace) + 1 :]
|
72
|
+
content = finder.loader._get_code(relative_path)
|
73
|
+
if content is not None:
|
74
|
+
return StringIO(content)
|
75
|
+
|
76
|
+
return open(file_path, mode)
|
77
|
+
|
78
|
+
|
58
79
|
def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperator:
|
59
80
|
if isinstance(descriptor, EqualsExpression):
|
60
81
|
return "="
|
@@ -399,6 +420,29 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
|
|
399
420
|
dict_value = value.model_dump()
|
400
421
|
return serialize_value(display_context, dict_value)
|
401
422
|
|
423
|
+
if callable(value):
|
424
|
+
function_definition = compile_function_definition(value)
|
425
|
+
source_path = inspect.getsourcefile(value)
|
426
|
+
if source_path is not None:
|
427
|
+
with virtual_open(source_path) as f:
|
428
|
+
source_code = f.read()
|
429
|
+
else:
|
430
|
+
source_code = f"Source code not available for {value.__name__}"
|
431
|
+
|
432
|
+
return {
|
433
|
+
"type": "CONSTANT_VALUE",
|
434
|
+
"value": {
|
435
|
+
"type": "JSON",
|
436
|
+
"value": {
|
437
|
+
"type": "CODE_EXECUTION",
|
438
|
+
"name": function_definition.name,
|
439
|
+
"description": function_definition.description,
|
440
|
+
"definition": function_definition.model_dump(),
|
441
|
+
"src": source_code,
|
442
|
+
},
|
443
|
+
},
|
444
|
+
}
|
445
|
+
|
402
446
|
if not isinstance(value, BaseDescriptor):
|
403
447
|
vellum_value = primitive_to_vellum_value(value)
|
404
448
|
return {
|
@@ -67,9 +67,14 @@ def test_event_enricher_static_workflow(is_dynamic: bool, expected_config: Optio
|
|
67
67
|
# WHEN the event_enricher is called with mocked dependencies
|
68
68
|
event_enricher(event)
|
69
69
|
|
70
|
-
#
|
70
|
+
# THEN workflow_version_exec_config is set to the expected config
|
71
71
|
assert event.body.workflow_version_exec_config == expected_config
|
72
72
|
|
73
|
+
assert event.body.display_context is not None
|
74
|
+
assert hasattr(event.body.display_context, "node_displays")
|
75
|
+
assert hasattr(event.body.display_context, "workflow_inputs")
|
76
|
+
assert hasattr(event.body.display_context, "workflow_outputs")
|
77
|
+
|
73
78
|
|
74
79
|
def test_event_enricher_marks_subworkflow_deployment_as_dynamic():
|
75
80
|
"""Test that event_enricher treats subworkflow deployments as dynamic."""
|
@@ -109,3 +114,8 @@ def test_event_enricher_marks_subworkflow_deployment_as_dynamic():
|
|
109
114
|
|
110
115
|
assert hasattr(enriched_event.body, "workflow_version_exec_config")
|
111
116
|
assert enriched_event.body.workflow_version_exec_config is not None
|
117
|
+
|
118
|
+
assert enriched_event.body.display_context is not None
|
119
|
+
assert hasattr(enriched_event.body.display_context, "node_displays")
|
120
|
+
assert hasattr(enriched_event.body.display_context, "workflow_inputs")
|
121
|
+
assert hasattr(enriched_event.body.display_context, "workflow_outputs")
|
@@ -329,16 +329,18 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
329
329
|
continue
|
330
330
|
|
331
331
|
target_node_display = self.display_context.node_displays[unadorned_target_node]
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
332
|
+
entrypoint_edge_dict: Dict[str, Json] = {
|
333
|
+
"id": str(entrypoint_display.edge_display.id),
|
334
|
+
"source_node_id": str(entrypoint_node_id),
|
335
|
+
"source_handle_id": str(entrypoint_node_source_handle_id),
|
336
|
+
"target_node_id": str(target_node_display.node_id),
|
337
|
+
"target_handle_id": str(target_node_display.get_trigger_id()),
|
338
|
+
"type": "DEFAULT",
|
339
|
+
}
|
340
|
+
display_data = self._serialize_edge_display_data(entrypoint_display.edge_display)
|
341
|
+
if display_data is not None:
|
342
|
+
entrypoint_edge_dict["display_data"] = display_data
|
343
|
+
edges.append(entrypoint_edge_dict)
|
342
344
|
|
343
345
|
for (source_node_port, target_node), edge_display in self.display_context.edge_displays.items():
|
344
346
|
unadorned_source_node_port = get_unadorned_port(source_node_port)
|
@@ -353,18 +355,20 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
353
355
|
source_node_port_display = self.display_context.port_displays[unadorned_source_node_port]
|
354
356
|
target_node_display = self.display_context.node_displays[unadorned_target_node]
|
355
357
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
358
|
+
regular_edge_dict: Dict[str, Json] = {
|
359
|
+
"id": str(edge_display.id),
|
360
|
+
"source_node_id": str(source_node_port_display.node_id),
|
361
|
+
"source_handle_id": str(source_node_port_display.id),
|
362
|
+
"target_node_id": str(target_node_display.node_id),
|
363
|
+
"target_handle_id": str(
|
364
|
+
target_node_display.get_target_handle_id_by_source_node_id(source_node_port_display.node_id)
|
365
|
+
),
|
366
|
+
"type": "DEFAULT",
|
367
|
+
}
|
368
|
+
display_data = self._serialize_edge_display_data(edge_display)
|
369
|
+
if display_data is not None:
|
370
|
+
regular_edge_dict["display_data"] = display_data
|
371
|
+
edges.append(regular_edge_dict)
|
368
372
|
|
369
373
|
edges.extend(synthetic_output_edges)
|
370
374
|
|
@@ -405,6 +409,12 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
405
409
|
"output_variables": output_variables,
|
406
410
|
}
|
407
411
|
|
412
|
+
def _serialize_edge_display_data(self, edge_display: EdgeDisplay) -> Optional[JsonObject]:
|
413
|
+
"""Serialize edge display data, returning None if no display data is present."""
|
414
|
+
if edge_display.z_index is not None:
|
415
|
+
return {"z_index": edge_display.z_index}
|
416
|
+
return None
|
417
|
+
|
408
418
|
def _apply_auto_layout(self, nodes_dict_list: List[Dict[str, Any]], edges: List[Json]) -> None:
|
409
419
|
"""Apply auto-layout to nodes that are all positioned at (0,0)."""
|
410
420
|
nodes_for_layout: List[Tuple[str, NodeDisplayData]] = []
|
File without changes
|
File without changes
|
File without changes
|