vellum-ai 0.10.3__py3-none-any.whl → 0.10.6__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 (61) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/events/tests/test_event.py +30 -0
  3. vellum/workflows/events/types.py +57 -3
  4. vellum/workflows/nodes/__init__.py +6 -7
  5. vellum/workflows/nodes/bases/base.py +0 -1
  6. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +2 -1
  7. vellum/workflows/nodes/core/map_node/node.py +1 -1
  8. vellum/workflows/nodes/core/retry_node/node.py +1 -0
  9. vellum/workflows/nodes/core/templating_node/node.py +5 -1
  10. vellum/workflows/nodes/core/try_node/node.py +66 -27
  11. vellum/workflows/nodes/core/try_node/tests/test_node.py +39 -8
  12. vellum/workflows/nodes/displayable/__init__.py +2 -0
  13. vellum/workflows/nodes/displayable/bases/api_node/node.py +3 -3
  14. vellum/workflows/nodes/displayable/code_execution_node/node.py +5 -2
  15. vellum/workflows/nodes/displayable/final_output_node/node.py +6 -2
  16. vellum/workflows/nodes/displayable/note_node/__init__.py +5 -0
  17. vellum/workflows/nodes/displayable/note_node/node.py +10 -0
  18. vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +10 -11
  19. vellum/workflows/nodes/utils.py +2 -0
  20. vellum/workflows/outputs/base.py +26 -2
  21. vellum/workflows/runner/runner.py +41 -27
  22. vellum/workflows/state/tests/test_state.py +2 -0
  23. vellum/workflows/types/tests/test_utils.py +9 -0
  24. vellum/workflows/types/utils.py +1 -1
  25. vellum/workflows/utils/vellum_variables.py +13 -1
  26. vellum/workflows/workflows/base.py +24 -1
  27. {vellum_ai-0.10.3.dist-info → vellum_ai-0.10.6.dist-info}/METADATA +8 -6
  28. {vellum_ai-0.10.3.dist-info → vellum_ai-0.10.6.dist-info}/RECORD +61 -56
  29. vellum_cli/CONTRIBUTING.md +66 -0
  30. vellum_cli/README.md +3 -0
  31. vellum_ee/workflows/display/base.py +2 -1
  32. vellum_ee/workflows/display/nodes/base_node_display.py +27 -4
  33. vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
  34. vellum_ee/workflows/display/nodes/vellum/api_node.py +3 -3
  35. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +4 -4
  36. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +86 -41
  37. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +3 -3
  38. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +4 -5
  39. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -9
  40. vellum_ee/workflows/display/nodes/vellum/map_node.py +5 -5
  41. vellum_ee/workflows/display/nodes/vellum/note_node.py +32 -0
  42. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +5 -5
  43. vellum_ee/workflows/display/nodes/vellum/search_node.py +6 -10
  44. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +2 -2
  45. vellum_ee/workflows/display/nodes/vellum/templating_node.py +4 -5
  46. vellum_ee/workflows/display/nodes/vellum/try_node.py +16 -4
  47. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +7 -3
  48. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +127 -101
  49. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +6 -5
  50. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +77 -64
  51. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +4 -3
  52. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +6 -6
  53. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +6 -6
  54. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +4 -3
  55. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +7 -6
  56. vellum_ee/workflows/display/workflows/base_workflow_display.py +14 -9
  57. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +2 -7
  58. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +18 -16
  59. {vellum_ai-0.10.3.dist-info → vellum_ai-0.10.6.dist-info}/LICENSE +0 -0
  60. {vellum_ai-0.10.3.dist-info → vellum_ai-0.10.6.dist-info}/WHEEL +0 -0
  61. {vellum_ai-0.10.3.dist-info → vellum_ai-0.10.6.dist-info}/entry_points.txt +0 -0
@@ -5,6 +5,7 @@ from pydantic import GetCoreSchemaHandler
5
5
  from pydantic_core import core_schema
6
6
 
7
7
  from vellum.workflows.constants import UNDEF
8
+ from vellum.workflows.descriptors.base import BaseDescriptor
8
9
  from vellum.workflows.references.output import OutputReference
9
10
  from vellum.workflows.types.utils import get_class_attr_names, infer_types
10
11
 
@@ -76,6 +77,23 @@ class BaseOutput(Generic[_Delta, _Accumulated]):
76
77
 
77
78
  return data
78
79
 
80
+ def __repr__(self) -> str:
81
+ if self.value is not UNDEF:
82
+ return f"{self.__class__.__name__}({self.name}={self.value})"
83
+ elif self.delta is not UNDEF:
84
+ return f"{self.__class__.__name__}({self.name}={self.delta})"
85
+ else:
86
+ return f"{self.__class__.__name__}(name='{self.name}')"
87
+
88
+ def __eq__(self, other: Any) -> bool:
89
+ if not isinstance(other, BaseOutput):
90
+ return False
91
+
92
+ return self.name == other.name and self.value == other.value and self.delta == other.delta
93
+
94
+ def __hash__(self) -> int:
95
+ return hash((self._name, self._value, self._value))
96
+
79
97
 
80
98
  @dataclass_transform(kw_only_default=True)
81
99
  class _BaseOutputsMeta(type):
@@ -175,7 +193,9 @@ class BaseOutputs(metaclass=_BaseOutputsMeta):
175
193
  if not isinstance(other, dict):
176
194
  return super().__eq__(other)
177
195
 
178
- outputs = {name: value for name, value in vars(self).items() if not name.startswith("_") and value is not UNDEF}
196
+ outputs = {
197
+ name: value for name, value in vars(self).items() if not name.startswith("_") and value is not UNDEF
198
+ }
179
199
  return outputs == other
180
200
 
181
201
  def __repr__(self) -> str:
@@ -184,7 +204,11 @@ class BaseOutputs(metaclass=_BaseOutputsMeta):
184
204
 
185
205
  def __iter__(self) -> Iterator[Tuple[OutputReference, Any]]:
186
206
  for output_descriptor in self.__class__:
187
- yield (output_descriptor, getattr(self, output_descriptor.name, output_descriptor.instance))
207
+ output_value = getattr(self, output_descriptor.name, UNDEF)
208
+ if isinstance(output_value, BaseDescriptor):
209
+ output_value = UNDEF
210
+
211
+ yield (output_descriptor, output_value)
188
212
 
189
213
  def __getitem__(self, key: str) -> Any:
190
214
  return getattr(self, key)
@@ -170,32 +170,37 @@ class WorkflowRunner(Generic[StateType]):
170
170
  streaming_output_queues: Dict[str, Queue] = {}
171
171
  outputs = node.Outputs()
172
172
 
173
+ def initiate_node_streaming_output(output: BaseOutput) -> None:
174
+ streaming_output_queues[output.name] = Queue()
175
+ output_descriptor = OutputReference(
176
+ name=output.name,
177
+ types=(type(output.delta),),
178
+ instance=None,
179
+ outputs_class=node.Outputs,
180
+ )
181
+ node.state.meta.node_outputs[output_descriptor] = streaming_output_queues[output.name]
182
+ self._work_item_event_queue.put(
183
+ WorkItemEvent(
184
+ node=node,
185
+ event=NodeExecutionStreamingEvent(
186
+ trace_id=node.state.meta.trace_id,
187
+ span_id=span_id,
188
+ body=NodeExecutionStreamingBody(
189
+ node_definition=node.__class__,
190
+ output=BaseOutput(name=output.name),
191
+ ),
192
+ ),
193
+ invoked_ports=invoked_ports,
194
+ )
195
+ )
196
+
173
197
  for output in node_run_response:
174
198
  invoked_ports = output > ports
175
- if not output.is_fulfilled:
199
+ if output.is_initiated:
200
+ initiate_node_streaming_output(output)
201
+ elif output.is_streaming:
176
202
  if output.name not in streaming_output_queues:
177
- streaming_output_queues[output.name] = Queue()
178
- output_descriptor = OutputReference(
179
- name=output.name,
180
- types=(type(output.delta),),
181
- instance=None,
182
- outputs_class=node.Outputs,
183
- )
184
- node.state.meta.node_outputs[output_descriptor] = streaming_output_queues[output.name]
185
- self._work_item_event_queue.put(
186
- WorkItemEvent(
187
- node=node,
188
- event=NodeExecutionStreamingEvent(
189
- trace_id=node.state.meta.trace_id,
190
- span_id=span_id,
191
- body=NodeExecutionStreamingBody(
192
- node_definition=node.__class__,
193
- output=BaseOutput(name=output.name),
194
- ),
195
- ),
196
- invoked_ports=invoked_ports,
197
- )
198
- )
203
+ initiate_node_streaming_output(output)
199
204
 
200
205
  streaming_output_queues[output.name].put(output.delta)
201
206
  self._work_item_event_queue.put(
@@ -212,7 +217,7 @@ class WorkflowRunner(Generic[StateType]):
212
217
  invoked_ports=invoked_ports,
213
218
  )
214
219
  )
215
- else:
220
+ elif output.is_fulfilled:
216
221
  if output.name in streaming_output_queues:
217
222
  streaming_output_queues[output.name].put(UNDEF)
218
223
 
@@ -233,6 +238,11 @@ class WorkflowRunner(Generic[StateType]):
233
238
  )
234
239
 
235
240
  for descriptor, output_value in outputs:
241
+ if output_value is UNDEF:
242
+ if descriptor in node.state.meta.node_outputs:
243
+ del node.state.meta.node_outputs[descriptor]
244
+ continue
245
+
236
246
  node.state.meta.node_outputs[descriptor] = output_value
237
247
 
238
248
  invoked_ports = ports(outputs, node.state)
@@ -540,11 +550,15 @@ class WorkflowRunner(Generic[StateType]):
540
550
  )
541
551
 
542
552
  def stream(self) -> WorkflowEventStream:
543
- background_thread = Thread(target=self._run_background_thread)
553
+ background_thread = Thread(
554
+ target=self._run_background_thread, name=f"{self.workflow.__class__.__name__}.background_thread"
555
+ )
544
556
  background_thread.start()
545
557
 
546
558
  if self._cancel_signal:
547
- cancel_thread = Thread(target=self._run_cancel_thread)
559
+ cancel_thread = Thread(
560
+ target=self._run_cancel_thread, name=f"{self.workflow.__class__.__name__}.cancel_thread"
561
+ )
548
562
  cancel_thread.start()
549
563
 
550
564
  event: WorkflowEvent
@@ -557,7 +571,7 @@ class WorkflowRunner(Generic[StateType]):
557
571
  self._initial_state.meta.is_terminated = False
558
572
 
559
573
  # The extra level of indirection prevents the runner from waiting on the caller to consume the event stream
560
- stream_thread = Thread(target=self._stream)
574
+ stream_thread = Thread(target=self._stream, name=f"{self.workflow.__class__.__name__}.stream_thread")
561
575
  stream_thread.start()
562
576
 
563
577
  while stream_thread.is_alive():
@@ -1,3 +1,4 @@
1
+ import pytest
1
2
  from collections import defaultdict
2
3
  from copy import deepcopy
3
4
  import json
@@ -76,6 +77,7 @@ def test_state_deepcopy():
76
77
  assert deepcopied_state.meta.node_outputs == state.meta.node_outputs
77
78
 
78
79
 
80
+ @pytest.mark.skip(reason="https://app.shortcut.com/vellum/story/5654")
79
81
  def test_state_deepcopy__with_node_output_updates():
80
82
  # GIVEN an initial state instance
81
83
  state = MockState(foo="bar")
@@ -1,6 +1,8 @@
1
1
  import pytest
2
2
  from typing import ClassVar, Generic, List, TypeVar, Union
3
3
 
4
+ from vellum.workflows.nodes.bases.base import BaseNode
5
+ from vellum.workflows.nodes.core.try_node.node import TryNode
4
6
  from vellum.workflows.outputs.base import BaseOutputs
5
7
  from vellum.workflows.references.output import OutputReference
6
8
  from vellum.workflows.types.utils import get_class_attr_names, infer_types
@@ -30,6 +32,11 @@ class ExampleGenericClass(Generic[T]):
30
32
  class ExampleInheritedClass(ExampleClass):
31
33
  theta: int
32
34
 
35
+ @TryNode.wrap()
36
+ class ExampleNode(BaseNode):
37
+ class Outputs(BaseNode.Outputs):
38
+ iota: str
39
+
33
40
 
34
41
  @pytest.mark.parametrize(
35
42
  "cls, attr_name, expected_type",
@@ -45,6 +52,7 @@ class ExampleInheritedClass(ExampleClass):
45
52
  (ExampleInheritedClass, "theta", (int,)),
46
53
  (ExampleInheritedClass, "alpha", (str,)),
47
54
  (ExampleInheritedClass, "beta", (int,)),
55
+ (ExampleNode.Outputs, "iota", (str,)),
48
56
  ],
49
57
  ids=[
50
58
  "str",
@@ -58,6 +66,7 @@ class ExampleInheritedClass(ExampleClass):
58
66
  "inherited_int",
59
67
  "inherited_parent_annotation",
60
68
  "inherited_parent_class_var",
69
+ "try_node_output",
61
70
  ],
62
71
  )
63
72
  def test_infer_types(cls, attr_name, expected_type):
@@ -1,6 +1,7 @@
1
1
  from copy import deepcopy
2
2
  from datetime import datetime
3
3
  import importlib
4
+ import sys
4
5
  from typing import (
5
6
  Any,
6
7
  ClassVar,
@@ -18,7 +19,6 @@ from typing import (
18
19
  )
19
20
 
20
21
  from vellum import ArrayVellumValue, ArrayVellumValueRequest, ChatMessagePromptBlock
21
-
22
22
  from vellum.workflows.descriptors.base import BaseDescriptor
23
23
  from vellum.workflows.types.core import Json, SpecialGenericAlias, UnderGenericAlias, UnionGenericAlias
24
24
 
@@ -1,3 +1,4 @@
1
+ import typing
1
2
  from typing import List, Tuple, Type, Union, get_args, get_origin
2
3
 
3
4
  from vellum import (
@@ -17,8 +18,8 @@ from vellum import (
17
18
  VellumValueRequest,
18
19
  VellumVariableType,
19
20
  )
20
-
21
21
  from vellum.workflows.descriptors.base import BaseDescriptor
22
+ from vellum.workflows.types.core import Json
22
23
 
23
24
 
24
25
  def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -> VellumVariableType:
@@ -32,6 +33,17 @@ def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -
32
33
  return "JSON"
33
34
 
34
35
  if len(types) != 1:
36
+ # Check explicitly for our internal JSON type.
37
+ # Matches the type found at vellum.workflows.utils.vellum_variables.Json
38
+ if types == [
39
+ bool,
40
+ int,
41
+ float,
42
+ str,
43
+ typing.List[typing.ForwardRef('Json')], # type: ignore [misc]
44
+ typing.Dict[str, typing.ForwardRef('Json')], # type: ignore [misc]
45
+ ]:
46
+ return "JSON"
35
47
  raise ValueError(f"Expected Descriptor to only have one type, got {types}")
36
48
 
37
49
  type_ = type_.types[0]
@@ -35,11 +35,17 @@ from vellum.workflows.emitters.base import BaseWorkflowEmitter
35
35
  from vellum.workflows.errors import VellumError, VellumErrorCode
36
36
  from vellum.workflows.events.node import (
37
37
  NodeExecutionFulfilledBody,
38
+ NodeExecutionFulfilledEvent,
38
39
  NodeExecutionInitiatedBody,
40
+ NodeExecutionInitiatedEvent,
39
41
  NodeExecutionPausedBody,
42
+ NodeExecutionPausedEvent,
40
43
  NodeExecutionRejectedBody,
44
+ NodeExecutionRejectedEvent,
41
45
  NodeExecutionResumedBody,
46
+ NodeExecutionResumedEvent,
42
47
  NodeExecutionStreamingBody,
48
+ NodeExecutionStreamingEvent,
43
49
  )
44
50
  from vellum.workflows.events.types import WorkflowEventType
45
51
  from vellum.workflows.events.workflow import (
@@ -55,6 +61,7 @@ from vellum.workflows.events.workflow import (
55
61
  WorkflowExecutionResumedBody,
56
62
  WorkflowExecutionResumedEvent,
57
63
  WorkflowExecutionStreamingBody,
64
+ WorkflowExecutionStreamingEvent,
58
65
  )
59
66
  from vellum.workflows.graph import Graph
60
67
  from vellum.workflows.inputs.base import BaseInputs
@@ -204,7 +211,9 @@ class BaseWorkflow(Generic[WorkflowInputsType, StateType], metaclass=_BaseWorkfl
204
211
  trace_id=uuid4(),
205
212
  span_id=uuid4(),
206
213
  body=WorkflowExecutionRejectedBody(
207
- error=VellumError(code=VellumErrorCode.INTERNAL_ERROR, message="Initiated event was never emitted"),
214
+ error=VellumError(
215
+ code=VellumErrorCode.INTERNAL_ERROR, message="Initiated event was never emitted"
216
+ ),
208
217
  workflow_definition=self.__class__,
209
218
  ),
210
219
  )
@@ -363,3 +372,17 @@ NodeExecutionRejectedBody.model_rebuild()
363
372
  NodeExecutionPausedBody.model_rebuild()
364
373
  NodeExecutionResumedBody.model_rebuild()
365
374
  NodeExecutionStreamingBody.model_rebuild()
375
+
376
+ WorkflowExecutionInitiatedEvent.model_rebuild()
377
+ WorkflowExecutionFulfilledEvent.model_rebuild()
378
+ WorkflowExecutionRejectedEvent.model_rebuild()
379
+ WorkflowExecutionPausedEvent.model_rebuild()
380
+ WorkflowExecutionResumedEvent.model_rebuild()
381
+ WorkflowExecutionStreamingEvent.model_rebuild()
382
+
383
+ NodeExecutionInitiatedEvent.model_rebuild()
384
+ NodeExecutionFulfilledEvent.model_rebuild()
385
+ NodeExecutionRejectedEvent.model_rebuild()
386
+ NodeExecutionPausedEvent.model_rebuild()
387
+ NodeExecutionResumedEvent.model_rebuild()
388
+ NodeExecutionStreamingEvent.model_rebuild()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.10.3
3
+ Version: 0.10.6
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -63,7 +63,6 @@ Description-Content-Type: text/markdown
63
63
 
64
64
  # Introduction
65
65
 
66
-
67
66
  [Vellum](https://www.vellum.ai/) is the end-to-end development platform for building production-grade AI applications
68
67
 
69
68
  ### Core Features
@@ -85,22 +84,21 @@ Description-Content-Type: text/markdown
85
84
  - [Contributing](#contributing)
86
85
  - [Open-source vs paid](#open-source-vs-paid)
87
86
 
88
-
89
87
  ## Get Started
88
+
90
89
  Most functionality within the SDKs here requires a Vellum account and API key. To sign up, [talk to us](https://www.vellum.ai/landing-pages/request-demo)
91
90
  or visit our [pricing page](https://www.vellum.ai/pricing).
92
91
 
93
92
  Even without a Vellum account, you can use the Workflows SDK to define the control flow of your AI systems. [Learn
94
93
  more below](#workflows-sdk).
95
94
 
96
-
97
-
98
95
  ## Client SDK
96
+
99
97
  The Vellum Client SDK, found within `src/client` is a low-level client used to interact directly with the Vellum API.
100
98
  Learn more and get started by visiting the [Vellum Client SDK README](/src/vellum/client/README.md).
101
99
 
102
-
103
100
  ## Workflows SDK
101
+
104
102
  The Vellum Workflows SDK is a high-level framework for defining and debugging the control flow of AI systems. At
105
103
  it's core, it's a powerful workflow engine with syntactic sugar for intuitively defining graphs, the nodes within,
106
104
  and the relationships between them.
@@ -111,6 +109,9 @@ and debugging via a UI.
111
109
 
112
110
  To learn more and get started, visit the [Vellum Workflows SDK README](/src/vellum/workflows/README.md).
113
111
 
112
+ ## Contributing
113
+
114
+ See the [CONTRIBUTING.md](/CONTRIBUTING.md) for information on how to contribute to the Vellum SDKs.
114
115
 
115
116
  ## Open-Source vs. Paid
116
117
 
@@ -118,3 +119,4 @@ This repo is available under the [MIT expat license](https://github.com/vellum-a
118
119
  for the `ee` directory (which has its [license here](https://github.com/vellum-ai/vellum-python-sdks/blob/main/ee/LICENSE)) if applicable.
119
120
 
120
121
  To learn more, [book a demo](https://www.vellum.ai/landing-pages/request-demo) or see our [pricing page](https://www.vellum.ai/pricing).
122
+