vellum-ai 0.14.7__py3-none-any.whl → 0.14.9__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 (44) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +2 -0
  4. vellum/client/types/document_prompt_block.py +29 -0
  5. vellum/client/types/prompt_block.py +2 -0
  6. vellum/types/document_prompt_block.py +3 -0
  7. vellum/workflows/descriptors/base.py +6 -0
  8. vellum/workflows/descriptors/tests/test_utils.py +14 -0
  9. vellum/workflows/events/tests/test_event.py +40 -0
  10. vellum/workflows/events/workflow.py +20 -1
  11. vellum/workflows/expressions/greater_than.py +15 -8
  12. vellum/workflows/expressions/greater_than_or_equal_to.py +14 -8
  13. vellum/workflows/expressions/less_than.py +14 -8
  14. vellum/workflows/expressions/less_than_or_equal_to.py +14 -8
  15. vellum/workflows/expressions/parse_json.py +30 -0
  16. vellum/workflows/expressions/tests/__init__.py +0 -0
  17. vellum/workflows/expressions/tests/test_expressions.py +310 -0
  18. vellum/workflows/expressions/tests/test_parse_json.py +31 -0
  19. vellum/workflows/nodes/bases/base.py +5 -2
  20. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +34 -2
  21. vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
  22. vellum/workflows/nodes/displayable/code_execution_node/node.py +18 -8
  23. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +53 -0
  24. vellum/workflows/runner/runner.py +33 -4
  25. vellum/workflows/state/encoder.py +2 -1
  26. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/METADATA +1 -1
  27. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/RECORD +44 -38
  28. vellum_cli/__init__.py +9 -2
  29. vellum_cli/config.py +1 -0
  30. vellum_cli/init.py +6 -2
  31. vellum_cli/pull.py +1 -0
  32. vellum_cli/tests/test_init.py +194 -76
  33. vellum_cli/tests/test_pull.py +8 -0
  34. vellum_cli/tests/test_push.py +1 -0
  35. vellum_ee/workflows/display/nodes/base_node_display.py +4 -0
  36. vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +114 -0
  37. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +118 -3
  38. vellum_ee/workflows/display/types.py +1 -14
  39. vellum_ee/workflows/display/workflows/base_workflow_display.py +48 -19
  40. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +12 -0
  41. vellum_ee/workflows/tests/test_server.py +1 -0
  42. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/LICENSE +0 -0
  43. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/WHEEL +0 -0
  44. {vellum_ai-0.14.7.dist-info → vellum_ai-0.14.9.dist-info}/entry_points.txt +0 -0
vellum/__init__.py CHANGED
@@ -83,6 +83,7 @@ from .types import (
83
83
  DocumentIndexIndexingConfigRequest,
84
84
  DocumentIndexRead,
85
85
  DocumentProcessingState,
86
+ DocumentPromptBlock,
86
87
  DocumentRead,
87
88
  DocumentStatus,
88
89
  DocumentVellumValue,
@@ -623,6 +624,7 @@ __all__ = [
623
624
  "DocumentIndexRead",
624
625
  "DocumentIndexesListRequestStatus",
625
626
  "DocumentProcessingState",
627
+ "DocumentPromptBlock",
626
628
  "DocumentRead",
627
629
  "DocumentStatus",
628
630
  "DocumentVellumValue",
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.14.7",
21
+ "X-Fern-SDK-Version": "0.14.9",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -87,6 +87,7 @@ from .document_index_indexing_config import DocumentIndexIndexingConfig
87
87
  from .document_index_indexing_config_request import DocumentIndexIndexingConfigRequest
88
88
  from .document_index_read import DocumentIndexRead
89
89
  from .document_processing_state import DocumentProcessingState
90
+ from .document_prompt_block import DocumentPromptBlock
90
91
  from .document_read import DocumentRead
91
92
  from .document_status import DocumentStatus
92
93
  from .document_vellum_value import DocumentVellumValue
@@ -611,6 +612,7 @@ __all__ = [
611
612
  "DocumentIndexIndexingConfigRequest",
612
613
  "DocumentIndexRead",
613
614
  "DocumentProcessingState",
615
+ "DocumentPromptBlock",
614
616
  "DocumentRead",
615
617
  "DocumentStatus",
616
618
  "DocumentVellumValue",
@@ -0,0 +1,29 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ..core.pydantic_utilities import UniversalBaseModel
4
+ import typing
5
+ from .prompt_block_state import PromptBlockState
6
+ from .ephemeral_prompt_cache_config import EphemeralPromptCacheConfig
7
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
8
+ import pydantic
9
+
10
+
11
+ class DocumentPromptBlock(UniversalBaseModel):
12
+ """
13
+ A block that represents a document in a prompt template.
14
+ """
15
+
16
+ block_type: typing.Literal["DOCUMENT"] = "DOCUMENT"
17
+ state: typing.Optional[PromptBlockState] = None
18
+ cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
19
+ src: str
20
+ metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
21
+
22
+ if IS_PYDANTIC_V2:
23
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
24
+ else:
25
+
26
+ class Config:
27
+ frozen = True
28
+ smart_union = True
29
+ extra = pydantic.Extra.allow
@@ -8,6 +8,7 @@ from .rich_text_prompt_block import RichTextPromptBlock
8
8
  from .audio_prompt_block import AudioPromptBlock
9
9
  from .function_call_prompt_block import FunctionCallPromptBlock
10
10
  from .image_prompt_block import ImagePromptBlock
11
+ from .document_prompt_block import DocumentPromptBlock
11
12
  import typing
12
13
 
13
14
  if typing.TYPE_CHECKING:
@@ -20,4 +21,5 @@ PromptBlock = typing.Union[
20
21
  AudioPromptBlock,
21
22
  FunctionCallPromptBlock,
22
23
  ImagePromptBlock,
24
+ DocumentPromptBlock,
23
25
  ]
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.document_prompt_block import *
@@ -29,6 +29,7 @@ if TYPE_CHECKING:
29
29
  from vellum.workflows.expressions.not_between import NotBetweenExpression
30
30
  from vellum.workflows.expressions.not_in import NotInExpression
31
31
  from vellum.workflows.expressions.or_ import OrExpression
32
+ from vellum.workflows.expressions.parse_json import ParseJsonExpression
32
33
  from vellum.workflows.nodes.bases import BaseNode
33
34
  from vellum.workflows.state.base import BaseState
34
35
 
@@ -349,3 +350,8 @@ class BaseDescriptor(Generic[_T]):
349
350
  from vellum.workflows.expressions.is_not_blank import IsNotBlankExpression
350
351
 
351
352
  return IsNotBlankExpression(expression=self)
353
+
354
+ def parse_json(self) -> "ParseJsonExpression[_T]":
355
+ from vellum.workflows.expressions.parse_json import ParseJsonExpression
356
+
357
+ return ParseJsonExpression(expression=self)
@@ -96,6 +96,13 @@ class DummyNode(BaseNode[FixtureState]):
96
96
  ).does_not_contain("test"),
97
97
  False,
98
98
  ),
99
+ (ConstantValueReference('{"foo": "bar"}').parse_json(), {"foo": "bar"}),
100
+ (ConstantValueReference('{"foo": "bar"}').parse_json()["foo"], "bar"),
101
+ (ConstantValueReference("[1, 2, 3]").parse_json(), [1, 2, 3]),
102
+ (ConstantValueReference("[1, 2, 3]").parse_json()[0], 1),
103
+ (ConstantValueReference(b'{"foo": "bar"}').parse_json(), {"foo": "bar"}),
104
+ (ConstantValueReference(bytearray(b'{"foo": "bar"}')).parse_json(), {"foo": "bar"}),
105
+ (ConstantValueReference(b'{"key": "\xf0\x9f\x8c\x9f"}').parse_json(), {"key": "🌟"}),
99
106
  ],
100
107
  ids=[
101
108
  "or",
@@ -143,6 +150,13 @@ class DummyNode(BaseNode[FixtureState]):
143
150
  "list_index",
144
151
  "error_contains",
145
152
  "error_does_not_contain",
153
+ "parse_json_constant",
154
+ "parse_json_accessor",
155
+ "parse_json_list",
156
+ "parse_json_list_index",
157
+ "parse_json_bytes",
158
+ "parse_json_bytearray",
159
+ "parse_json_bytes_with_utf8_chars",
146
160
  ],
147
161
  )
148
162
  def test_resolve_value__happy_path(descriptor, expected_value):
@@ -4,6 +4,7 @@ from uuid import UUID
4
4
 
5
5
  from deepdiff import DeepDiff
6
6
 
7
+ from vellum.workflows.constants import undefined
7
8
  from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
8
9
  from vellum.workflows.events.node import (
9
10
  NodeExecutionFulfilledBody,
@@ -86,6 +87,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
86
87
  "inputs": {
87
88
  "foo": "bar",
88
89
  },
90
+ "display_context": None,
89
91
  },
90
92
  "parent": None,
91
93
  },
@@ -330,6 +332,43 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
330
332
  "parent": None,
331
333
  },
332
334
  ),
335
+ (
336
+ NodeExecutionFulfilledEvent(
337
+ id=UUID("123e4567-e89b-12d3-a456-426614174000"),
338
+ timestamp=datetime(2024, 1, 1, 12, 0, 0),
339
+ trace_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
340
+ span_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
341
+ body=NodeExecutionFulfilledBody(
342
+ node_definition=MockNode,
343
+ outputs=MockNode.Outputs(
344
+ example=undefined, # type: ignore[arg-type]
345
+ ),
346
+ invoked_ports={MockNode.Ports.default},
347
+ ),
348
+ ),
349
+ {
350
+ "id": "123e4567-e89b-12d3-a456-426614174000",
351
+ "api_version": "2024-10-25",
352
+ "timestamp": "2024-01-01T12:00:00",
353
+ "trace_id": "123e4567-e89b-12d3-a456-426614174000",
354
+ "span_id": "123e4567-e89b-12d3-a456-426614174000",
355
+ "name": "node.execution.fulfilled",
356
+ "body": {
357
+ "node_definition": {
358
+ "id": mock_node_uuid,
359
+ "name": "MockNode",
360
+ "module": module_root + ["events", "tests", "test_event"],
361
+ },
362
+ "outputs": {},
363
+ "invoked_ports": [
364
+ {
365
+ "name": "default",
366
+ }
367
+ ],
368
+ },
369
+ "parent": None,
370
+ },
371
+ ),
333
372
  ],
334
373
  ids=[
335
374
  "workflow.execution.initiated",
@@ -339,6 +378,7 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
339
378
  "workflow.execution.rejected",
340
379
  "node.execution.streaming",
341
380
  "node.execution.fulfilled",
381
+ "fulfilled_node_with_undefined_outputs",
342
382
  ],
343
383
  )
344
384
  def test_event_serialization(event, expected_json):
@@ -1,4 +1,5 @@
1
- from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Iterable, Literal, Type, Union
1
+ from uuid import UUID
2
+ from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, Iterable, Literal, Optional, Type, Union
2
3
 
3
4
  from pydantic import field_serializer
4
5
 
@@ -38,8 +39,26 @@ class _BaseWorkflowEvent(BaseEvent):
38
39
  return self.body.workflow_definition
39
40
 
40
41
 
42
+ class NodeDisplay(UniversalBaseModel):
43
+ input_display: Dict[str, UUID]
44
+ output_display: Dict[str, UUID]
45
+ port_display: Dict[str, UUID]
46
+
47
+
48
+ class WorkflowEventDisplayContext(UniversalBaseModel):
49
+ node_displays: Dict[str, NodeDisplay]
50
+ workflow_inputs: Dict[str, UUID]
51
+ workflow_outputs: Dict[str, UUID]
52
+
53
+
41
54
  class WorkflowExecutionInitiatedBody(_BaseWorkflowExecutionBody, Generic[InputsType]):
42
55
  inputs: InputsType
56
+ # It is still the responsibility of the workflow server to populate this context. The SDK's
57
+ # Workflow Runner will always leave this field None.
58
+ #
59
+ # It's conceivable in a future where all `id`s are agnostic to display and reside in a third location,
60
+ # that the Workflow Runner can begin populating this field then.
61
+ display_context: Optional[WorkflowEventDisplayContext] = None
43
62
 
44
63
  @field_serializer("inputs")
45
64
  def serialize_inputs(self, inputs: InputsType, _info: Any) -> Dict[str, Any]:
@@ -1,10 +1,21 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
5
6
  from vellum.workflows.descriptors.utils import resolve_value
6
7
  from vellum.workflows.state.base import BaseState
7
8
 
9
+
10
+ @runtime_checkable
11
+ class SupportsGreaterThan(Protocol):
12
+ def __gt__(self, other: Any) -> bool: ...
13
+
14
+
15
+ def has_gt(obj: Any) -> TypeGuard[SupportsGreaterThan]:
16
+ return hasattr(obj, "__gt__")
17
+
18
+
8
19
  LHS = TypeVar("LHS")
9
20
  RHS = TypeVar("RHS")
10
21
 
@@ -21,14 +32,10 @@ class GreaterThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
32
  self._rhs = rhs
22
33
 
23
34
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the > operator
25
- # https://app.shortcut.com/vellum/story/4658
26
35
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
36
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
37
+
38
+ if not has_gt(lhs):
39
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '>' operator")
33
40
 
34
41
  return lhs > rhs
@@ -1,4 +1,5 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
@@ -9,6 +10,15 @@ LHS = TypeVar("LHS")
9
10
  RHS = TypeVar("RHS")
10
11
 
11
12
 
13
+ @runtime_checkable
14
+ class SupportsGreaterThanOrEqualTo(Protocol):
15
+ def __ge__(self, other: Any) -> bool: ...
16
+
17
+
18
+ def has_ge(obj: Any) -> TypeGuard[SupportsGreaterThanOrEqualTo]:
19
+ return hasattr(obj, "__ge__")
20
+
21
+
12
22
  class GreaterThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
13
23
  def __init__(
14
24
  self,
@@ -21,14 +31,10 @@ class GreaterThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
31
  self._rhs = rhs
22
32
 
23
33
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the >= operator
25
- # https://app.shortcut.com/vellum/story/4658
26
34
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
35
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
36
+
37
+ if not has_ge(lhs):
38
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '>=' operator")
33
39
 
34
40
  return lhs >= rhs
@@ -1,4 +1,5 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
@@ -9,6 +10,15 @@ LHS = TypeVar("LHS")
9
10
  RHS = TypeVar("RHS")
10
11
 
11
12
 
13
+ @runtime_checkable
14
+ class SupportsLessThan(Protocol):
15
+ def __lt__(self, other: Any) -> bool: ...
16
+
17
+
18
+ def has_lt(obj: Any) -> TypeGuard[SupportsLessThan]:
19
+ return hasattr(obj, "__lt__")
20
+
21
+
12
22
  class LessThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
13
23
  def __init__(
14
24
  self,
@@ -21,14 +31,10 @@ class LessThanExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
31
  self._rhs = rhs
22
32
 
23
33
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the < operator
25
- # https://app.shortcut.com/vellum/story/4658
26
34
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
35
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
36
+
37
+ if not has_lt(lhs):
38
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '<' operator")
33
39
 
34
40
  return lhs < rhs
@@ -1,4 +1,5 @@
1
- from typing import Generic, TypeVar, Union
1
+ from typing import Any, Generic, Protocol, TypeVar, Union, runtime_checkable
2
+ from typing_extensions import TypeGuard
2
3
 
3
4
  from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.descriptors.exceptions import InvalidExpressionException
@@ -9,6 +10,15 @@ LHS = TypeVar("LHS")
9
10
  RHS = TypeVar("RHS")
10
11
 
11
12
 
13
+ @runtime_checkable
14
+ class SupportsLessThanOrEqualTo(Protocol):
15
+ def __le__(self, other: Any) -> bool: ...
16
+
17
+
18
+ def has_le(obj: Any) -> TypeGuard[SupportsLessThanOrEqualTo]:
19
+ return hasattr(obj, "__le__")
20
+
21
+
12
22
  class LessThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
13
23
  def __init__(
14
24
  self,
@@ -21,14 +31,10 @@ class LessThanOrEqualToExpression(BaseDescriptor[bool], Generic[LHS, RHS]):
21
31
  self._rhs = rhs
22
32
 
23
33
  def resolve(self, state: "BaseState") -> bool:
24
- # Support any type that implements the <= operator
25
- # https://app.shortcut.com/vellum/story/4658
26
34
  lhs = resolve_value(self._lhs, state)
27
- if not isinstance(lhs, (int, float)):
28
- raise InvalidExpressionException(f"Expected a numeric lhs value, got: {lhs.__class__.__name__}")
29
-
30
35
  rhs = resolve_value(self._rhs, state)
31
- if not isinstance(rhs, (int, float)):
32
- raise InvalidExpressionException(f"Expected a numeric rhs value, got: {rhs.__class__.__name__}")
36
+
37
+ if not has_le(lhs):
38
+ raise InvalidExpressionException(f"'{lhs.__class__.__name__}' must support the '<=' operator")
33
39
 
34
40
  return lhs <= rhs
@@ -0,0 +1,30 @@
1
+ import json
2
+ from typing import Any, Generic, TypeVar, Union
3
+
4
+ from vellum.workflows.descriptors.base import BaseDescriptor
5
+ from vellum.workflows.descriptors.exceptions import InvalidExpressionException
6
+ from vellum.workflows.descriptors.utils import resolve_value
7
+ from vellum.workflows.state.base import BaseState
8
+
9
+ _T = TypeVar("_T")
10
+
11
+
12
+ class ParseJsonExpression(BaseDescriptor[Any], Generic[_T]):
13
+ def __init__(
14
+ self,
15
+ *,
16
+ expression: Union[BaseDescriptor[_T], _T],
17
+ ) -> None:
18
+ super().__init__(name=f"parse_json({expression})", types=(Any,)) # type: ignore[arg-type]
19
+ self._expression = expression
20
+
21
+ def resolve(self, state: "BaseState") -> Any:
22
+ value = resolve_value(self._expression, state)
23
+
24
+ if not isinstance(value, (str, bytes, bytearray)):
25
+ raise InvalidExpressionException(f"Expected a string, but got {value} of type {type(value)}")
26
+
27
+ try:
28
+ return json.loads(value)
29
+ except json.JSONDecodeError as e:
30
+ raise InvalidExpressionException(f"Failed to parse JSON: {e}") from e
File without changes