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.
Files changed (96) hide show
  1. vellum/__init__.py +48 -0
  2. vellum/client/core/client_wrapper.py +2 -2
  3. vellum/client/resources/workflows/client.py +20 -0
  4. vellum/client/resources/workflows/raw_client.py +20 -0
  5. vellum/client/types/__init__.py +48 -0
  6. vellum/client/types/audio_input.py +30 -0
  7. vellum/client/types/code_executor_input.py +8 -0
  8. vellum/client/types/document_input.py +30 -0
  9. vellum/client/types/image_input.py +30 -0
  10. vellum/client/types/named_scenario_input_audio_variable_value_request.py +22 -0
  11. vellum/client/types/named_scenario_input_document_variable_value_request.py +22 -0
  12. vellum/client/types/named_scenario_input_image_variable_value_request.py +22 -0
  13. vellum/client/types/named_scenario_input_request.py +8 -0
  14. vellum/client/types/named_scenario_input_video_variable_value_request.py +22 -0
  15. vellum/client/types/named_test_case_audio_variable_value.py +26 -0
  16. vellum/client/types/named_test_case_audio_variable_value_request.py +26 -0
  17. vellum/client/types/named_test_case_document_variable_value.py +22 -0
  18. vellum/client/types/named_test_case_document_variable_value_request.py +22 -0
  19. vellum/client/types/named_test_case_image_variable_value.py +22 -0
  20. vellum/client/types/named_test_case_image_variable_value_request.py +22 -0
  21. vellum/client/types/named_test_case_variable_value.py +8 -0
  22. vellum/client/types/named_test_case_variable_value_request.py +8 -0
  23. vellum/client/types/named_test_case_video_variable_value.py +22 -0
  24. vellum/client/types/named_test_case_video_variable_value_request.py +22 -0
  25. vellum/client/types/node_execution_span_attributes.py +1 -0
  26. vellum/client/types/scenario_input.py +11 -1
  27. vellum/client/types/scenario_input_audio_variable_value.py +22 -0
  28. vellum/client/types/scenario_input_document_variable_value.py +22 -0
  29. vellum/client/types/scenario_input_image_variable_value.py +22 -0
  30. vellum/client/types/scenario_input_video_variable_value.py +22 -0
  31. vellum/client/types/span_link.py +1 -1
  32. vellum/client/types/span_link_type_enum.py +1 -1
  33. vellum/client/types/test_case_audio_variable_value.py +27 -0
  34. vellum/client/types/test_case_document_variable_value.py +27 -0
  35. vellum/client/types/test_case_image_variable_value.py +27 -0
  36. vellum/client/types/test_case_variable_value.py +8 -0
  37. vellum/client/types/test_case_video_variable_value.py +27 -0
  38. vellum/client/types/video_input.py +30 -0
  39. vellum/client/types/workflow_push_deployment_config_request.py +1 -0
  40. vellum/types/audio_input.py +3 -0
  41. vellum/types/document_input.py +3 -0
  42. vellum/types/image_input.py +3 -0
  43. vellum/types/named_scenario_input_audio_variable_value_request.py +3 -0
  44. vellum/types/named_scenario_input_document_variable_value_request.py +3 -0
  45. vellum/types/named_scenario_input_image_variable_value_request.py +3 -0
  46. vellum/types/named_scenario_input_video_variable_value_request.py +3 -0
  47. vellum/types/named_test_case_audio_variable_value.py +3 -0
  48. vellum/types/named_test_case_audio_variable_value_request.py +3 -0
  49. vellum/types/named_test_case_document_variable_value.py +3 -0
  50. vellum/types/named_test_case_document_variable_value_request.py +3 -0
  51. vellum/types/named_test_case_image_variable_value.py +3 -0
  52. vellum/types/named_test_case_image_variable_value_request.py +3 -0
  53. vellum/types/named_test_case_video_variable_value.py +3 -0
  54. vellum/types/named_test_case_video_variable_value_request.py +3 -0
  55. vellum/types/scenario_input_audio_variable_value.py +3 -0
  56. vellum/types/scenario_input_document_variable_value.py +3 -0
  57. vellum/types/scenario_input_image_variable_value.py +3 -0
  58. vellum/types/scenario_input_video_variable_value.py +3 -0
  59. vellum/types/test_case_audio_variable_value.py +3 -0
  60. vellum/types/test_case_document_variable_value.py +3 -0
  61. vellum/types/test_case_image_variable_value.py +3 -0
  62. vellum/types/test_case_video_variable_value.py +3 -0
  63. vellum/types/video_input.py +3 -0
  64. vellum/workflows/events/tests/test_event.py +9 -0
  65. vellum/workflows/events/types.py +3 -1
  66. vellum/workflows/integrations/tests/test_mcp_service.py +40 -1
  67. vellum/workflows/nodes/core/templating_node/node.py +3 -2
  68. vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +129 -0
  69. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +12 -0
  70. vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +41 -0
  71. vellum/workflows/nodes/displayable/bases/utils.py +38 -1
  72. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -20
  73. vellum/workflows/nodes/displayable/inline_prompt_node/node.py +3 -26
  74. vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +3 -25
  75. vellum/workflows/nodes/utils.py +26 -1
  76. vellum/workflows/resolvers/base.py +18 -1
  77. vellum/workflows/resolvers/resolver.py +42 -0
  78. vellum/workflows/resolvers/tests/test_resolver.py +59 -0
  79. vellum/workflows/types/definition.py +1 -0
  80. vellum/workflows/utils/functions.py +4 -0
  81. vellum/workflows/utils/tests/test_functions.py +6 -3
  82. vellum/workflows/workflows/base.py +3 -0
  83. {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/METADATA +1 -1
  84. {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/RECORD +96 -46
  85. vellum_cli/__init__.py +6 -0
  86. vellum_cli/config.py +2 -0
  87. vellum_cli/push.py +3 -0
  88. vellum_cli/tests/test_pull.py +2 -0
  89. vellum_cli/tests/test_push.py +39 -0
  90. vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +2 -0
  91. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +1 -0
  92. vellum_ee/workflows/display/utils/events.py +19 -1
  93. vellum_ee/workflows/display/utils/tests/test_events.py +42 -0
  94. {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/LICENSE +0 -0
  95. {vellum_ai-1.2.3.dist-info → vellum_ai-1.2.5.dist-info}/WHEEL +0 -0
  96. {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
- string_outputs = []
49
- json_output = None
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
- string_outputs = []
52
- json_output = None
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)
@@ -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 DictWrapper, ListWrapper, StringValueWrapper
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 typing import Iterator
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": {"a": {"type": "integer"}, "b": {"type": "string"}},
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0