vellum-ai 0.10.3__py3-none-any.whl → 0.10.6__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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
+