vellum-ai 1.2.3__py3-none-any.whl → 1.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vellum/__init__.py +48 -0
- vellum/client/core/client_wrapper.py +2 -2
- vellum/client/resources/workflows/client.py +20 -0
- vellum/client/resources/workflows/raw_client.py +20 -0
- vellum/client/types/__init__.py +48 -0
- vellum/client/types/audio_input.py +30 -0
- vellum/client/types/code_executor_input.py +8 -0
- vellum/client/types/document_input.py +30 -0
- vellum/client/types/image_input.py +30 -0
- vellum/client/types/named_scenario_input_audio_variable_value_request.py +22 -0
- vellum/client/types/named_scenario_input_document_variable_value_request.py +22 -0
- vellum/client/types/named_scenario_input_image_variable_value_request.py +22 -0
- vellum/client/types/named_scenario_input_request.py +8 -0
- vellum/client/types/named_scenario_input_video_variable_value_request.py +22 -0
- vellum/client/types/named_test_case_audio_variable_value.py +26 -0
- vellum/client/types/named_test_case_audio_variable_value_request.py +26 -0
- vellum/client/types/named_test_case_document_variable_value.py +22 -0
- vellum/client/types/named_test_case_document_variable_value_request.py +22 -0
- vellum/client/types/named_test_case_image_variable_value.py +22 -0
- vellum/client/types/named_test_case_image_variable_value_request.py +22 -0
- vellum/client/types/named_test_case_variable_value.py +8 -0
- vellum/client/types/named_test_case_variable_value_request.py +8 -0
- vellum/client/types/named_test_case_video_variable_value.py +22 -0
- vellum/client/types/named_test_case_video_variable_value_request.py +22 -0
- vellum/client/types/node_execution_span_attributes.py +1 -0
- vellum/client/types/scenario_input.py +11 -1
- vellum/client/types/scenario_input_audio_variable_value.py +22 -0
- vellum/client/types/scenario_input_document_variable_value.py +22 -0
- vellum/client/types/scenario_input_image_variable_value.py +22 -0
- vellum/client/types/scenario_input_video_variable_value.py +22 -0
- vellum/client/types/span_link.py +1 -1
- vellum/client/types/span_link_type_enum.py +1 -1
- vellum/client/types/test_case_audio_variable_value.py +27 -0
- vellum/client/types/test_case_document_variable_value.py +27 -0
- vellum/client/types/test_case_image_variable_value.py +27 -0
- vellum/client/types/test_case_variable_value.py +8 -0
- vellum/client/types/test_case_video_variable_value.py +27 -0
- vellum/client/types/video_input.py +30 -0
- vellum/client/types/workflow_push_deployment_config_request.py +1 -0
- vellum/types/audio_input.py +3 -0
- vellum/types/document_input.py +3 -0
- vellum/types/image_input.py +3 -0
- vellum/types/named_scenario_input_audio_variable_value_request.py +3 -0
- vellum/types/named_scenario_input_document_variable_value_request.py +3 -0
- vellum/types/named_scenario_input_image_variable_value_request.py +3 -0
- vellum/types/named_scenario_input_video_variable_value_request.py +3 -0
- vellum/types/named_test_case_audio_variable_value.py +3 -0
- vellum/types/named_test_case_audio_variable_value_request.py +3 -0
- vellum/types/named_test_case_document_variable_value.py +3 -0
- vellum/types/named_test_case_document_variable_value_request.py +3 -0
- vellum/types/named_test_case_image_variable_value.py +3 -0
- vellum/types/named_test_case_image_variable_value_request.py +3 -0
- vellum/types/named_test_case_video_variable_value.py +3 -0
- vellum/types/named_test_case_video_variable_value_request.py +3 -0
- vellum/types/scenario_input_audio_variable_value.py +3 -0
- vellum/types/scenario_input_document_variable_value.py +3 -0
- vellum/types/scenario_input_image_variable_value.py +3 -0
- vellum/types/scenario_input_video_variable_value.py +3 -0
- vellum/types/test_case_audio_variable_value.py +3 -0
- vellum/types/test_case_document_variable_value.py +3 -0
- vellum/types/test_case_image_variable_value.py +3 -0
- vellum/types/test_case_video_variable_value.py +3 -0
- vellum/types/video_input.py +3 -0
- vellum/workflows/events/tests/test_event.py +9 -0
- vellum/workflows/events/types.py +3 -1
- vellum/workflows/integrations/tests/test_mcp_service.py +40 -1
- vellum/workflows/nodes/core/templating_node/node.py +3 -2
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +129 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +12 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +41 -0
- vellum/workflows/nodes/displayable/bases/utils.py +38 -1
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -20
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +3 -26
- vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +3 -25
- vellum/workflows/nodes/utils.py +26 -1
- vellum/workflows/resolvers/base.py +18 -1
- vellum/workflows/resolvers/resolver.py +42 -0
- vellum/workflows/resolvers/tests/test_resolver.py +59 -0
- vellum/workflows/types/definition.py +1 -0
- vellum/workflows/utils/functions.py +4 -0
- vellum/workflows/utils/tests/test_functions.py +6 -3
- vellum/workflows/workflows/base.py +3 -0
- {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/METADATA +1 -1
- {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/RECORD +96 -46
- vellum_cli/__init__.py +6 -0
- vellum_cli/config.py +2 -0
- vellum_cli/push.py +3 -0
- vellum_cli/tests/test_pull.py +2 -0
- vellum_cli/tests/test_push.py +39 -0
- vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +2 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +1 -0
- vellum_ee/workflows/display/utils/events.py +19 -1
- vellum_ee/workflows/display/utils/tests/test_events.py +42 -0
- {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/LICENSE +0 -0
- {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/WHEEL +0 -0
- {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,10 @@
|
|
1
|
-
import json
|
2
1
|
from typing import Any, Dict, Generic, Iterator, Type, Union
|
3
2
|
|
4
3
|
from vellum.workflows.constants import undefined
|
5
4
|
from vellum.workflows.errors import WorkflowErrorCode
|
6
5
|
from vellum.workflows.exceptions import NodeException
|
7
6
|
from vellum.workflows.nodes.displayable.bases import BaseInlinePromptNode as BaseInlinePromptNode
|
7
|
+
from vellum.workflows.nodes.displayable.bases.utils import process_additional_prompt_outputs
|
8
8
|
from vellum.workflows.outputs import BaseOutput
|
9
9
|
from vellum.workflows.types import MergeBehavior
|
10
10
|
from vellum.workflows.types.generics import StateType
|
@@ -45,31 +45,8 @@ class InlinePromptNode(BaseInlinePromptNode[StateType], Generic[StateType]):
|
|
45
45
|
code=WorkflowErrorCode.INTERNAL_ERROR,
|
46
46
|
)
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
for output in outputs:
|
52
|
-
if output.value is None:
|
53
|
-
continue
|
54
|
-
|
55
|
-
if output.type == "STRING":
|
56
|
-
string_outputs.append(output.value)
|
57
|
-
try:
|
58
|
-
json_output = json.loads(output.value)
|
59
|
-
except (json.JSONDecodeError, TypeError):
|
60
|
-
pass
|
61
|
-
elif output.type == "JSON":
|
62
|
-
string_outputs.append(json.dumps(output.value, indent=4))
|
63
|
-
json_output = output.value
|
64
|
-
elif output.type == "FUNCTION_CALL":
|
65
|
-
string_outputs.append(output.value.model_dump_json(indent=4))
|
66
|
-
elif output.type == "THINKING":
|
67
|
-
continue
|
68
|
-
else:
|
69
|
-
string_outputs.append(output.value.message)
|
70
|
-
|
71
|
-
value = "\n".join(string_outputs)
|
72
|
-
yield BaseOutput(name="text", value=value)
|
48
|
+
text_output, json_output = process_additional_prompt_outputs(outputs)
|
49
|
+
yield BaseOutput(name="text", value=text_output)
|
73
50
|
|
74
51
|
if json_output:
|
75
52
|
yield BaseOutput(name="json", value=json_output)
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import json
|
2
1
|
from typing import Any, Dict, Iterator, Type, Union
|
3
2
|
|
4
3
|
from vellum.workflows.constants import undefined
|
5
4
|
from vellum.workflows.errors import WorkflowErrorCode
|
6
5
|
from vellum.workflows.exceptions import NodeException
|
7
6
|
from vellum.workflows.nodes.displayable.bases import BasePromptDeploymentNode as BasePromptDeploymentNode
|
7
|
+
from vellum.workflows.nodes.displayable.bases.utils import process_additional_prompt_outputs
|
8
8
|
from vellum.workflows.outputs import BaseOutput
|
9
9
|
from vellum.workflows.types import MergeBehavior
|
10
10
|
from vellum.workflows.types.generics import StateType
|
@@ -48,30 +48,8 @@ class PromptDeploymentNode(BasePromptDeploymentNode[StateType]):
|
|
48
48
|
code=WorkflowErrorCode.INTERNAL_ERROR,
|
49
49
|
)
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
for output in outputs:
|
55
|
-
if output.value is None:
|
56
|
-
continue
|
57
|
-
|
58
|
-
if output.type == "STRING":
|
59
|
-
string_outputs.append(output.value)
|
60
|
-
try:
|
61
|
-
json_output = json.loads(output.value)
|
62
|
-
except (json.JSONDecodeError, TypeError):
|
63
|
-
pass
|
64
|
-
elif output.type == "JSON":
|
65
|
-
string_outputs.append(json.dumps(output.value, indent=4))
|
66
|
-
elif output.type == "FUNCTION_CALL":
|
67
|
-
string_outputs.append(output.value.model_dump_json(indent=4))
|
68
|
-
elif output.type == "THINKING":
|
69
|
-
continue
|
70
|
-
else:
|
71
|
-
string_outputs.append(output.value.message)
|
72
|
-
|
73
|
-
value = "\n".join(string_outputs)
|
74
|
-
yield BaseOutput(name="text", value=value)
|
51
|
+
text_output, json_output = process_additional_prompt_outputs(outputs)
|
52
|
+
yield BaseOutput(name="text", value=text_output)
|
75
53
|
|
76
54
|
if json_output:
|
77
55
|
yield BaseOutput(name="json", value=json_output)
|
vellum/workflows/nodes/utils.py
CHANGED
@@ -15,7 +15,12 @@ from vellum.workflows.nodes import BaseNode
|
|
15
15
|
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
16
16
|
from vellum.workflows.ports.port import Port
|
17
17
|
from vellum.workflows.state.base import BaseState
|
18
|
-
from vellum.workflows.types.code_execution_node_wrappers import
|
18
|
+
from vellum.workflows.types.code_execution_node_wrappers import (
|
19
|
+
DictWrapper,
|
20
|
+
ListWrapper,
|
21
|
+
StringValueWrapper,
|
22
|
+
clean_for_dict_wrapper,
|
23
|
+
)
|
19
24
|
from vellum.workflows.types.core import Json
|
20
25
|
from vellum.workflows.types.generics import NodeType
|
21
26
|
|
@@ -261,3 +266,23 @@ def cast_to_output_type(result: Any, output_type: Any) -> Any:
|
|
261
266
|
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
262
267
|
message=f"Expected an output of type '{output_type_name}', but received '{result_type_name}'",
|
263
268
|
)
|
269
|
+
|
270
|
+
|
271
|
+
def wrap_inputs_for_backward_compatibility(inputs: Dict[str, Any]) -> Dict[str, Any]:
|
272
|
+
"""Wrap inputs with backward-compatible wrapper classes for legacy .value and .type support."""
|
273
|
+
|
274
|
+
def _wrap_single_value(value: Any) -> Any:
|
275
|
+
if isinstance(value, list):
|
276
|
+
return ListWrapper(
|
277
|
+
[
|
278
|
+
(
|
279
|
+
item.model_dump()
|
280
|
+
if isinstance(item, BaseModel)
|
281
|
+
else clean_for_dict_wrapper(item) if isinstance(item, (dict, list, str)) else item
|
282
|
+
)
|
283
|
+
for item in value
|
284
|
+
]
|
285
|
+
)
|
286
|
+
return clean_for_dict_wrapper(value)
|
287
|
+
|
288
|
+
return {name: _wrap_single_value(value) for name, value in inputs.items()}
|
@@ -1,11 +1,24 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
|
-
from
|
2
|
+
from uuid import UUID
|
3
|
+
from typing import TYPE_CHECKING, Iterator, Optional, Type
|
3
4
|
|
4
5
|
from vellum.workflows.events.workflow import WorkflowEvent
|
5
6
|
from vellum.workflows.state.base import BaseState
|
6
7
|
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from vellum.workflows.state.context import WorkflowContext
|
10
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
11
|
+
|
7
12
|
|
8
13
|
class BaseWorkflowResolver(ABC):
|
14
|
+
def __init__(self):
|
15
|
+
self._context: Optional["WorkflowContext"] = None
|
16
|
+
self._workflow_class: Optional[Type["BaseWorkflow"]] = None
|
17
|
+
|
18
|
+
def register_workflow_instance(self, workflow_instance: "BaseWorkflow") -> None:
|
19
|
+
self._workflow_class = type(workflow_instance)
|
20
|
+
self._context = workflow_instance.context
|
21
|
+
|
9
22
|
@abstractmethod
|
10
23
|
def get_latest_execution_events(self) -> Iterator[WorkflowEvent]:
|
11
24
|
pass
|
@@ -13,3 +26,7 @@ class BaseWorkflowResolver(ABC):
|
|
13
26
|
@abstractmethod
|
14
27
|
def get_state_snapshot_history(self) -> Iterator[BaseState]:
|
15
28
|
pass
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
def load_state(self, previous_execution_id: Optional[UUID] = None) -> Optional[BaseState]:
|
32
|
+
pass
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import logging
|
2
|
+
from uuid import UUID
|
3
|
+
from typing import Iterator, Optional
|
4
|
+
|
5
|
+
from vellum.workflows.events.workflow import WorkflowEvent
|
6
|
+
from vellum.workflows.resolvers.base import BaseWorkflowResolver
|
7
|
+
from vellum.workflows.state.base import BaseState, StateMeta
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class VellumResolver(BaseWorkflowResolver):
|
13
|
+
def get_latest_execution_events(self) -> Iterator[WorkflowEvent]:
|
14
|
+
return iter([])
|
15
|
+
|
16
|
+
def get_state_snapshot_history(self) -> Iterator[BaseState]:
|
17
|
+
return iter([])
|
18
|
+
|
19
|
+
def load_state(self, previous_execution_id: Optional[UUID] = None) -> Optional[BaseState]:
|
20
|
+
if previous_execution_id is None:
|
21
|
+
return None
|
22
|
+
|
23
|
+
if not self._context:
|
24
|
+
logger.warning("Cannot load state: No workflow context registered")
|
25
|
+
return None
|
26
|
+
|
27
|
+
client = self._context.vellum_client
|
28
|
+
response = client.workflow_executions.retrieve_workflow_execution_detail(
|
29
|
+
execution_id=str(previous_execution_id),
|
30
|
+
)
|
31
|
+
|
32
|
+
if response.state is None:
|
33
|
+
return None
|
34
|
+
|
35
|
+
meta = StateMeta.model_validate(response.state.pop("meta"))
|
36
|
+
|
37
|
+
if self._workflow_class:
|
38
|
+
state_class = self._workflow_class.get_state_class()
|
39
|
+
return state_class(**response.state, meta=meta)
|
40
|
+
else:
|
41
|
+
logger.warning("No workflow class registered, falling back to BaseState")
|
42
|
+
return BaseState(**response.state, meta=meta)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from unittest.mock import Mock
|
3
|
+
from uuid import uuid4
|
4
|
+
|
5
|
+
from vellum.client.types.workflow_execution_detail import WorkflowExecutionDetail
|
6
|
+
from vellum.workflows import BaseWorkflow
|
7
|
+
from vellum.workflows.inputs.base import BaseInputs
|
8
|
+
from vellum.workflows.resolvers.resolver import VellumResolver
|
9
|
+
from vellum.workflows.state.base import BaseState, NodeExecutionCache
|
10
|
+
from vellum.workflows.state.context import WorkflowContext
|
11
|
+
|
12
|
+
|
13
|
+
def test_load_state_with_context_success():
|
14
|
+
"""Test load_state successfully loads state when context and client are available."""
|
15
|
+
resolver = VellumResolver()
|
16
|
+
execution_id = uuid4()
|
17
|
+
|
18
|
+
class TestState(BaseState):
|
19
|
+
test_key: str = "test_value"
|
20
|
+
|
21
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, TestState]):
|
22
|
+
pass
|
23
|
+
|
24
|
+
# GIVEN a state dictionary that matches what the resolver expects
|
25
|
+
state_dict = {
|
26
|
+
"test_key": "test_value",
|
27
|
+
"meta": {
|
28
|
+
"workflow_definition": "MockWorkflow",
|
29
|
+
"id": str(uuid4()),
|
30
|
+
"span_id": str(uuid4()),
|
31
|
+
"updated_ts": datetime.now().isoformat(),
|
32
|
+
"workflow_inputs": BaseInputs(),
|
33
|
+
"external_inputs": {},
|
34
|
+
"node_outputs": {},
|
35
|
+
"node_execution_cache": NodeExecutionCache(),
|
36
|
+
"parent": None,
|
37
|
+
},
|
38
|
+
}
|
39
|
+
|
40
|
+
mock_response = WorkflowExecutionDetail(
|
41
|
+
span_id="test-span-id", start=datetime.now(), inputs=[], outputs=[], spans=[], state=state_dict
|
42
|
+
)
|
43
|
+
|
44
|
+
mock_client = Mock()
|
45
|
+
mock_client.workflow_executions.retrieve_workflow_execution_detail.return_value = mock_response
|
46
|
+
|
47
|
+
# AND context with the test workflow class
|
48
|
+
context = WorkflowContext(vellum_client=mock_client)
|
49
|
+
TestWorkflow(context=context, resolvers=[resolver])
|
50
|
+
|
51
|
+
result = resolver.load_state(previous_execution_id=execution_id)
|
52
|
+
|
53
|
+
# THEN should return an instance of TestWorkflow.State, not BaseState
|
54
|
+
assert isinstance(result, TestState)
|
55
|
+
assert result.test_key == "test_value"
|
56
|
+
|
57
|
+
mock_client.workflow_executions.retrieve_workflow_execution_detail.assert_called_once_with(
|
58
|
+
execution_id=str(execution_id)
|
59
|
+
)
|
@@ -121,6 +121,7 @@ class ComposioToolDefinition(UniversalBaseModel):
|
|
121
121
|
class MCPServer(UniversalBaseModel):
|
122
122
|
type: Literal["MCP_SERVER"] = "MCP_SERVER"
|
123
123
|
name: str
|
124
|
+
description: str = "" # We don't use this field, its for compatibility with UI
|
124
125
|
url: str
|
125
126
|
authorization_type: Optional[AuthorizationType] = None
|
126
127
|
bearer_token_value: Optional[Union[str, EnvironmentVariableReference]] = None
|
@@ -89,6 +89,10 @@ def compile_annotation(annotation: Optional[Any], defs: dict[str, Any]) -> dict:
|
|
89
89
|
# Mypy is incorrect here, the `annotation` attribute is defined on `FieldInfo`
|
90
90
|
field_annotation = field.annotation # type: ignore[attr-defined]
|
91
91
|
properties[field_name] = compile_annotation(field_annotation, defs)
|
92
|
+
|
93
|
+
if hasattr(field, "description") and field.description is not None:
|
94
|
+
properties[field_name]["description"] = field.description # type: ignore[attr-defined]
|
95
|
+
|
92
96
|
if field.default is PydanticUndefined:
|
93
97
|
required.append(field_name)
|
94
98
|
else:
|
@@ -4,7 +4,7 @@ from enum import Enum
|
|
4
4
|
from unittest.mock import Mock
|
5
5
|
from typing import Annotated, Dict, List, Literal, Optional, Tuple, Union
|
6
6
|
|
7
|
-
from pydantic import BaseModel
|
7
|
+
from pydantic import BaseModel, Field
|
8
8
|
|
9
9
|
from vellum.client.types.function_definition import FunctionDefinition
|
10
10
|
from vellum.client.types.string_vellum_value import StringVellumValue
|
@@ -206,7 +206,7 @@ def test_compile_function_definition__dataclasses():
|
|
206
206
|
def test_compile_function_definition__pydantic():
|
207
207
|
# GIVEN a function with a pydantic model
|
208
208
|
class MyPydanticModel(BaseModel):
|
209
|
-
a: int
|
209
|
+
a: int = Field(description="The first number")
|
210
210
|
b: str
|
211
211
|
|
212
212
|
def my_function(c: MyPydanticModel):
|
@@ -225,7 +225,10 @@ def test_compile_function_definition__pydantic():
|
|
225
225
|
"$defs": {
|
226
226
|
"MyPydanticModel": {
|
227
227
|
"type": "object",
|
228
|
-
"properties": {
|
228
|
+
"properties": {
|
229
|
+
"a": {"type": "integer", "description": "The first number"},
|
230
|
+
"b": {"type": "string"},
|
231
|
+
},
|
229
232
|
"required": ["a", "b"],
|
230
233
|
}
|
231
234
|
},
|
@@ -251,6 +251,9 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
251
251
|
for emitter in self.emitters:
|
252
252
|
emitter.register_context(self._context)
|
253
253
|
|
254
|
+
for resolver in self.resolvers:
|
255
|
+
resolver.register_workflow_instance(self)
|
256
|
+
|
254
257
|
self.validate()
|
255
258
|
|
256
259
|
@property
|