vellum-ai 0.14.8__py3-none-any.whl → 0.14.10__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 (35) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/utils/templating/render.py +4 -1
  3. vellum/workflows/descriptors/base.py +6 -0
  4. vellum/workflows/descriptors/tests/test_utils.py +14 -0
  5. vellum/workflows/events/tests/test_event.py +40 -0
  6. vellum/workflows/events/workflow.py +21 -1
  7. vellum/workflows/expressions/greater_than.py +15 -8
  8. vellum/workflows/expressions/greater_than_or_equal_to.py +14 -8
  9. vellum/workflows/expressions/less_than.py +14 -8
  10. vellum/workflows/expressions/less_than_or_equal_to.py +14 -8
  11. vellum/workflows/expressions/parse_json.py +30 -0
  12. vellum/workflows/expressions/tests/__init__.py +0 -0
  13. vellum/workflows/expressions/tests/test_expressions.py +310 -0
  14. vellum/workflows/expressions/tests/test_parse_json.py +31 -0
  15. vellum/workflows/nodes/bases/base.py +5 -2
  16. vellum/workflows/nodes/core/templating_node/node.py +0 -1
  17. vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +49 -0
  18. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +6 -7
  19. vellum/workflows/nodes/displayable/code_execution_node/node.py +18 -8
  20. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +53 -0
  21. vellum/workflows/nodes/utils.py +14 -3
  22. vellum/workflows/runner/runner.py +34 -9
  23. vellum/workflows/state/encoder.py +2 -1
  24. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.10.dist-info}/METADATA +1 -1
  25. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.10.dist-info}/RECORD +35 -31
  26. vellum_ee/workflows/display/nodes/base_node_display.py +4 -0
  27. vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +31 -0
  28. vellum_ee/workflows/display/types.py +1 -14
  29. vellum_ee/workflows/display/workflows/base_workflow_display.py +38 -18
  30. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +27 -0
  31. vellum_ee/workflows/tests/test_display_meta.py +2 -0
  32. vellum_ee/workflows/tests/test_server.py +1 -0
  33. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.10.dist-info}/LICENSE +0 -0
  34. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.10.dist-info}/WHEEL +0 -0
  35. {vellum_ai-0.14.8.dist-info → vellum_ai-0.14.10.dist-info}/entry_points.txt +0 -0
@@ -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.8",
21
+ "X-Fern-SDK-Version": "0.14.10",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -2,6 +2,7 @@ import json
2
2
  from typing import Any, Dict, Optional
3
3
 
4
4
  from jinja2.sandbox import SandboxedEnvironment
5
+ from pydantic import BaseModel
5
6
 
6
7
  from vellum.utils.templating.constants import FilterFunc
7
8
  from vellum.utils.templating.exceptions import JinjaTemplateError
@@ -12,6 +13,9 @@ def finalize(obj: Any) -> str:
12
13
  if isinstance(obj, (dict, list)):
13
14
  return json.dumps(obj, cls=DefaultStateEncoder)
14
15
 
16
+ if isinstance(obj, BaseModel):
17
+ return json.dumps(obj.model_dump(), cls=DefaultStateEncoder)
18
+
15
19
  return str(obj)
16
20
 
17
21
 
@@ -23,7 +27,6 @@ def render_sandboxed_jinja_template(
23
27
  jinja_globals: Optional[Dict[str, Any]] = None,
24
28
  ) -> str:
25
29
  """Render a Jinja template within a sandboxed environment."""
26
-
27
30
  try:
28
31
  environment = SandboxedEnvironment(
29
32
  keep_trailing_newline=True,
@@ -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,27 @@ class _BaseWorkflowEvent(BaseEvent):
38
39
  return self.body.workflow_definition
39
40
 
40
41
 
42
+ class NodeEventDisplayContext(UniversalBaseModel):
43
+ input_display: Dict[str, UUID]
44
+ output_display: Dict[str, UUID]
45
+ port_display: Dict[str, UUID]
46
+ subworkflow_display: Optional["WorkflowEventDisplayContext"] = None
47
+
48
+
49
+ class WorkflowEventDisplayContext(UniversalBaseModel):
50
+ node_displays: Dict[str, NodeEventDisplayContext]
51
+ workflow_inputs: Dict[str, UUID]
52
+ workflow_outputs: Dict[str, UUID]
53
+
54
+
41
55
  class WorkflowExecutionInitiatedBody(_BaseWorkflowExecutionBody, Generic[InputsType]):
42
56
  inputs: InputsType
57
+ # It is still the responsibility of the workflow server to populate this context. The SDK's
58
+ # Workflow Runner will always leave this field None.
59
+ #
60
+ # It's conceivable in a future where all `id`s are agnostic to display and reside in a third location,
61
+ # that the Workflow Runner can begin populating this field then.
62
+ display_context: Optional[WorkflowEventDisplayContext] = None
43
63
 
44
64
  @field_serializer("inputs")
45
65
  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