vellum-ai 0.14.26__py3-none-any.whl → 0.14.28__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 (28) hide show
  1. vellum/__init__.py +4 -0
  2. vellum/client/__init__.py +4 -0
  3. vellum/client/core/client_wrapper.py +1 -1
  4. vellum/client/resources/__init__.py +2 -0
  5. vellum/client/resources/documents/client.py +0 -14
  6. vellum/client/resources/prompts/__init__.py +2 -0
  7. vellum/client/resources/prompts/client.py +197 -0
  8. vellum/client/types/__init__.py +2 -0
  9. vellum/client/types/prompt_exec_config.py +37 -0
  10. vellum/resources/prompts/__init__.py +3 -0
  11. vellum/resources/prompts/client.py +3 -0
  12. vellum/types/prompt_exec_config.py +3 -0
  13. vellum/workflows/events/tests/test_event.py +29 -1
  14. vellum/workflows/events/types.py +62 -3
  15. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -2
  16. vellum/workflows/nodes/displayable/guardrail_node/node.py +10 -11
  17. vellum/workflows/nodes/displayable/guardrail_node/test_node.py +38 -0
  18. vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +13 -2
  19. vellum/workflows/runner/runner.py +2 -0
  20. {vellum_ai-0.14.26.dist-info → vellum_ai-0.14.28.dist-info}/METADATA +1 -1
  21. {vellum_ai-0.14.26.dist-info → vellum_ai-0.14.28.dist-info}/RECORD +28 -21
  22. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +2 -1
  23. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +80 -1
  24. vellum_ee/workflows/server/virtual_file_loader.py +16 -4
  25. vellum_ee/workflows/tests/test_server.py +79 -0
  26. {vellum_ai-0.14.26.dist-info → vellum_ai-0.14.28.dist-info}/LICENSE +0 -0
  27. {vellum_ai-0.14.26.dist-info → vellum_ai-0.14.28.dist-info}/WHEEL +0 -0
  28. {vellum_ai-0.14.26.dist-info → vellum_ai-0.14.28.dist-info}/entry_points.txt +0 -0
vellum/__init__.py CHANGED
@@ -300,6 +300,7 @@ from .types import (
300
300
  PromptDeploymentExpandMetaRequest,
301
301
  PromptDeploymentInputRequest,
302
302
  PromptDeploymentParentContext,
303
+ PromptExecConfig,
303
304
  PromptExecutionMeta,
304
305
  PromptNodeExecutionMeta,
305
306
  PromptNodeResult,
@@ -589,6 +590,7 @@ from .resources import (
589
590
  metric_definitions,
590
591
  ml_models,
591
592
  organizations,
593
+ prompts,
592
594
  sandboxes,
593
595
  test_suite_runs,
594
596
  test_suites,
@@ -910,6 +912,7 @@ __all__ = [
910
912
  "PromptDeploymentExpandMetaRequest",
911
913
  "PromptDeploymentInputRequest",
912
914
  "PromptDeploymentParentContext",
915
+ "PromptExecConfig",
913
916
  "PromptExecutionMeta",
914
917
  "PromptNodeExecutionMeta",
915
918
  "PromptNodeResult",
@@ -1193,6 +1196,7 @@ __all__ = [
1193
1196
  "metric_definitions",
1194
1197
  "ml_models",
1195
1198
  "organizations",
1199
+ "prompts",
1196
1200
  "sandboxes",
1197
1201
  "test_suite_runs",
1198
1202
  "test_suites",
vellum/client/__init__.py CHANGED
@@ -13,6 +13,7 @@ from .resources.folder_entities.client import FolderEntitiesClient
13
13
  from .resources.metric_definitions.client import MetricDefinitionsClient
14
14
  from .resources.ml_models.client import MlModelsClient
15
15
  from .resources.organizations.client import OrganizationsClient
16
+ from .resources.prompts.client import PromptsClient
16
17
  from .resources.sandboxes.client import SandboxesClient
17
18
  from .resources.test_suite_runs.client import TestSuiteRunsClient
18
19
  from .resources.test_suites.client import TestSuitesClient
@@ -69,6 +70,7 @@ from .resources.folder_entities.client import AsyncFolderEntitiesClient
69
70
  from .resources.metric_definitions.client import AsyncMetricDefinitionsClient
70
71
  from .resources.ml_models.client import AsyncMlModelsClient
71
72
  from .resources.organizations.client import AsyncOrganizationsClient
73
+ from .resources.prompts.client import AsyncPromptsClient
72
74
  from .resources.sandboxes.client import AsyncSandboxesClient
73
75
  from .resources.test_suite_runs.client import AsyncTestSuiteRunsClient
74
76
  from .resources.test_suites.client import AsyncTestSuitesClient
@@ -145,6 +147,7 @@ class Vellum:
145
147
  self.metric_definitions = MetricDefinitionsClient(client_wrapper=self._client_wrapper)
146
148
  self.ml_models = MlModelsClient(client_wrapper=self._client_wrapper)
147
149
  self.organizations = OrganizationsClient(client_wrapper=self._client_wrapper)
150
+ self.prompts = PromptsClient(client_wrapper=self._client_wrapper)
148
151
  self.sandboxes = SandboxesClient(client_wrapper=self._client_wrapper)
149
152
  self.test_suite_runs = TestSuiteRunsClient(client_wrapper=self._client_wrapper)
150
153
  self.test_suites = TestSuitesClient(client_wrapper=self._client_wrapper)
@@ -1486,6 +1489,7 @@ class AsyncVellum:
1486
1489
  self.metric_definitions = AsyncMetricDefinitionsClient(client_wrapper=self._client_wrapper)
1487
1490
  self.ml_models = AsyncMlModelsClient(client_wrapper=self._client_wrapper)
1488
1491
  self.organizations = AsyncOrganizationsClient(client_wrapper=self._client_wrapper)
1492
+ self.prompts = AsyncPromptsClient(client_wrapper=self._client_wrapper)
1489
1493
  self.sandboxes = AsyncSandboxesClient(client_wrapper=self._client_wrapper)
1490
1494
  self.test_suite_runs = AsyncTestSuiteRunsClient(client_wrapper=self._client_wrapper)
1491
1495
  self.test_suites = AsyncTestSuitesClient(client_wrapper=self._client_wrapper)
@@ -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.26",
21
+ "X-Fern-SDK-Version": "0.14.28",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -10,6 +10,7 @@ from . import (
10
10
  metric_definitions,
11
11
  ml_models,
12
12
  organizations,
13
+ prompts,
13
14
  sandboxes,
14
15
  test_suite_runs,
15
16
  test_suites,
@@ -42,6 +43,7 @@ __all__ = [
42
43
  "metric_definitions",
43
44
  "ml_models",
44
45
  "organizations",
46
+ "prompts",
45
47
  "sandboxes",
46
48
  "test_suite_runs",
47
49
  "test_suites",
@@ -277,13 +277,6 @@ class DocumentsClient:
277
277
 
278
278
  **Note:** Uses a base url of `https://documents.vellum.ai`.
279
279
 
280
- This is a multipart/form-data request. The `contents` field should be a file upload. It also expects a JSON body with the following fields:
281
- - `add_to_index_names: list[str]` - Optionally include the names of all indexes that you'd like this document to be included in
282
- - `external_id: str | None` - Optionally include an external ID for this document. This is useful if you want to re-upload the same document later when its contents change and would like it to be re-indexed.
283
- - `label: str` - A human-friendly name for this document. Typically the filename.
284
- - `keywords: list[str] | None` - Optionally include a list of keywords that'll be associated with this document. Used when performing keyword searches.
285
- - `metadata: dict[str, Any]` - A stringified JSON object containing any metadata associated with the document that you'd like to filter upon later.
286
-
287
280
  Parameters
288
281
  ----------
289
282
  label : str
@@ -673,13 +666,6 @@ class AsyncDocumentsClient:
673
666
 
674
667
  **Note:** Uses a base url of `https://documents.vellum.ai`.
675
668
 
676
- This is a multipart/form-data request. The `contents` field should be a file upload. It also expects a JSON body with the following fields:
677
- - `add_to_index_names: list[str]` - Optionally include the names of all indexes that you'd like this document to be included in
678
- - `external_id: str | None` - Optionally include an external ID for this document. This is useful if you want to re-upload the same document later when its contents change and would like it to be re-indexed.
679
- - `label: str` - A human-friendly name for this document. Typically the filename.
680
- - `keywords: list[str] | None` - Optionally include a list of keywords that'll be associated with this document. Used when performing keyword searches.
681
- - `metadata: dict[str, Any]` - A stringified JSON object containing any metadata associated with the document that you'd like to filter upon later.
682
-
683
669
  Parameters
684
670
  ----------
685
671
  label : str
@@ -0,0 +1,2 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
@@ -0,0 +1,197 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ...core.client_wrapper import SyncClientWrapper
4
+ import typing
5
+ from ...core.request_options import RequestOptions
6
+ from ...types.prompt_exec_config import PromptExecConfig
7
+ from ...core.jsonable_encoder import jsonable_encoder
8
+ from ...core.pydantic_utilities import parse_obj_as
9
+ from ...errors.bad_request_error import BadRequestError
10
+ from ...errors.not_found_error import NotFoundError
11
+ from json.decoder import JSONDecodeError
12
+ from ...core.api_error import ApiError
13
+ from ...core.client_wrapper import AsyncClientWrapper
14
+
15
+
16
+ class PromptsClient:
17
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
18
+ self._client_wrapper = client_wrapper
19
+
20
+ def pull(
21
+ self,
22
+ id: str,
23
+ *,
24
+ prompt_variant_id: typing.Optional[str] = None,
25
+ request_options: typing.Optional[RequestOptions] = None,
26
+ ) -> PromptExecConfig:
27
+ """
28
+ Used to pull the definition of a Prompt from Vellum.
29
+
30
+ Parameters
31
+ ----------
32
+ id : str
33
+ The ID of the Prompt to pull from. Prompt Sandbox IDs are currently supported.
34
+
35
+ prompt_variant_id : typing.Optional[str]
36
+ The ID of the Prompt Variant within a Prompt Sandbox to pull. Must be included if providing the ID of a Prompt Sandbox.
37
+
38
+ request_options : typing.Optional[RequestOptions]
39
+ Request-specific configuration.
40
+
41
+ Returns
42
+ -------
43
+ PromptExecConfig
44
+
45
+
46
+ Examples
47
+ --------
48
+ from vellum import Vellum
49
+
50
+ client = Vellum(
51
+ api_key="YOUR_API_KEY",
52
+ )
53
+ client.prompts.pull(
54
+ id="id",
55
+ )
56
+ """
57
+ _response = self._client_wrapper.httpx_client.request(
58
+ f"v1/prompts/{jsonable_encoder(id)}/pull",
59
+ base_url=self._client_wrapper.get_environment().default,
60
+ method="GET",
61
+ params={
62
+ "prompt_variant_id": prompt_variant_id,
63
+ },
64
+ headers={
65
+ "Accept": "application/json",
66
+ },
67
+ request_options=request_options,
68
+ )
69
+ try:
70
+ if 200 <= _response.status_code < 300:
71
+ return typing.cast(
72
+ PromptExecConfig,
73
+ parse_obj_as(
74
+ type_=PromptExecConfig, # type: ignore
75
+ object_=_response.json(),
76
+ ),
77
+ )
78
+ if _response.status_code == 400:
79
+ raise BadRequestError(
80
+ typing.cast(
81
+ typing.Optional[typing.Any],
82
+ parse_obj_as(
83
+ type_=typing.Optional[typing.Any], # type: ignore
84
+ object_=_response.json(),
85
+ ),
86
+ )
87
+ )
88
+ if _response.status_code == 404:
89
+ raise NotFoundError(
90
+ typing.cast(
91
+ typing.Optional[typing.Any],
92
+ parse_obj_as(
93
+ type_=typing.Optional[typing.Any], # type: ignore
94
+ object_=_response.json(),
95
+ ),
96
+ )
97
+ )
98
+ _response_json = _response.json()
99
+ except JSONDecodeError:
100
+ raise ApiError(status_code=_response.status_code, body=_response.text)
101
+ raise ApiError(status_code=_response.status_code, body=_response_json)
102
+
103
+
104
+ class AsyncPromptsClient:
105
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
106
+ self._client_wrapper = client_wrapper
107
+
108
+ async def pull(
109
+ self,
110
+ id: str,
111
+ *,
112
+ prompt_variant_id: typing.Optional[str] = None,
113
+ request_options: typing.Optional[RequestOptions] = None,
114
+ ) -> PromptExecConfig:
115
+ """
116
+ Used to pull the definition of a Prompt from Vellum.
117
+
118
+ Parameters
119
+ ----------
120
+ id : str
121
+ The ID of the Prompt to pull from. Prompt Sandbox IDs are currently supported.
122
+
123
+ prompt_variant_id : typing.Optional[str]
124
+ The ID of the Prompt Variant within a Prompt Sandbox to pull. Must be included if providing the ID of a Prompt Sandbox.
125
+
126
+ request_options : typing.Optional[RequestOptions]
127
+ Request-specific configuration.
128
+
129
+ Returns
130
+ -------
131
+ PromptExecConfig
132
+
133
+
134
+ Examples
135
+ --------
136
+ import asyncio
137
+
138
+ from vellum import AsyncVellum
139
+
140
+ client = AsyncVellum(
141
+ api_key="YOUR_API_KEY",
142
+ )
143
+
144
+
145
+ async def main() -> None:
146
+ await client.prompts.pull(
147
+ id="id",
148
+ )
149
+
150
+
151
+ asyncio.run(main())
152
+ """
153
+ _response = await self._client_wrapper.httpx_client.request(
154
+ f"v1/prompts/{jsonable_encoder(id)}/pull",
155
+ base_url=self._client_wrapper.get_environment().default,
156
+ method="GET",
157
+ params={
158
+ "prompt_variant_id": prompt_variant_id,
159
+ },
160
+ headers={
161
+ "Accept": "application/json",
162
+ },
163
+ request_options=request_options,
164
+ )
165
+ try:
166
+ if 200 <= _response.status_code < 300:
167
+ return typing.cast(
168
+ PromptExecConfig,
169
+ parse_obj_as(
170
+ type_=PromptExecConfig, # type: ignore
171
+ object_=_response.json(),
172
+ ),
173
+ )
174
+ if _response.status_code == 400:
175
+ raise BadRequestError(
176
+ typing.cast(
177
+ typing.Optional[typing.Any],
178
+ parse_obj_as(
179
+ type_=typing.Optional[typing.Any], # type: ignore
180
+ object_=_response.json(),
181
+ ),
182
+ )
183
+ )
184
+ if _response.status_code == 404:
185
+ raise NotFoundError(
186
+ typing.cast(
187
+ typing.Optional[typing.Any],
188
+ parse_obj_as(
189
+ type_=typing.Optional[typing.Any], # type: ignore
190
+ object_=_response.json(),
191
+ ),
192
+ )
193
+ )
194
+ _response_json = _response.json()
195
+ except JSONDecodeError:
196
+ raise ApiError(status_code=_response.status_code, body=_response.text)
197
+ raise ApiError(status_code=_response.status_code, body=_response_json)
@@ -308,6 +308,7 @@ from .prompt_block_state import PromptBlockState
308
308
  from .prompt_deployment_expand_meta_request import PromptDeploymentExpandMetaRequest
309
309
  from .prompt_deployment_input_request import PromptDeploymentInputRequest
310
310
  from .prompt_deployment_parent_context import PromptDeploymentParentContext
311
+ from .prompt_exec_config import PromptExecConfig
311
312
  from .prompt_execution_meta import PromptExecutionMeta
312
313
  from .prompt_node_execution_meta import PromptNodeExecutionMeta
313
314
  from .prompt_node_result import PromptNodeResult
@@ -892,6 +893,7 @@ __all__ = [
892
893
  "PromptDeploymentExpandMetaRequest",
893
894
  "PromptDeploymentInputRequest",
894
895
  "PromptDeploymentParentContext",
896
+ "PromptExecConfig",
895
897
  "PromptExecutionMeta",
896
898
  "PromptNodeExecutionMeta",
897
899
  "PromptNodeResult",
@@ -0,0 +1,37 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from __future__ import annotations
4
+ from ..core.pydantic_utilities import UniversalBaseModel
5
+ from .array_vellum_value import ArrayVellumValue
6
+ from .chat_message_prompt_block import ChatMessagePromptBlock
7
+ import typing
8
+ from .vellum_variable import VellumVariable
9
+ from .prompt_parameters import PromptParameters
10
+ from .prompt_settings import PromptSettings
11
+ from .prompt_block import PromptBlock
12
+ from .function_definition import FunctionDefinition
13
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
14
+ import pydantic
15
+ from ..core.pydantic_utilities import update_forward_refs
16
+
17
+
18
+ class PromptExecConfig(UniversalBaseModel):
19
+ ml_model: str
20
+ input_variables: typing.List[VellumVariable]
21
+ parameters: PromptParameters
22
+ settings: typing.Optional[PromptSettings] = None
23
+ blocks: typing.List[PromptBlock]
24
+ functions: typing.Optional[typing.List[FunctionDefinition]] = None
25
+
26
+ if IS_PYDANTIC_V2:
27
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
28
+ else:
29
+
30
+ class Config:
31
+ frozen = True
32
+ smart_union = True
33
+ extra = pydantic.Extra.allow
34
+
35
+
36
+ update_forward_refs(ArrayVellumValue, PromptExecConfig=PromptExecConfig)
37
+ update_forward_refs(ChatMessagePromptBlock, PromptExecConfig=PromptExecConfig)
@@ -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.resources.prompts import *
@@ -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.resources.prompts.client import *
@@ -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.prompt_exec_config import *
@@ -4,6 +4,7 @@ from uuid import UUID
4
4
 
5
5
  from deepdiff import DeepDiff
6
6
 
7
+ from vellum.client.core.pydantic_utilities import UniversalBaseModel
7
8
  from vellum.workflows.constants import undefined
8
9
  from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode
9
10
  from vellum.workflows.events.node import (
@@ -14,7 +15,7 @@ from vellum.workflows.events.node import (
14
15
  NodeExecutionStreamingBody,
15
16
  NodeExecutionStreamingEvent,
16
17
  )
17
- from vellum.workflows.events.types import NodeParentContext, WorkflowParentContext
18
+ from vellum.workflows.events.types import NodeParentContext, ParentContext, WorkflowParentContext
18
19
  from vellum.workflows.events.workflow import (
19
20
  WorkflowExecutionFulfilledBody,
20
21
  WorkflowExecutionFulfilledEvent,
@@ -419,3 +420,30 @@ mock_node_uuid = str(uuid4_from_hash(MockNode.__qualname__))
419
420
  )
420
421
  def test_event_serialization(event, expected_json):
421
422
  assert not DeepDiff(event.model_dump(mode="json"), expected_json)
423
+
424
+
425
+ def test_parent_context__deserialize_from_json__invalid_parent_context():
426
+ # GIVEN an event with a parent context that Vellum is introducing in the future
427
+ data = {
428
+ "foo": "bar",
429
+ "parent": {
430
+ "type": "SOME_FUTURE_ENTITY",
431
+ "span_id": "123e4567-e89b-12d3-a456-426614174000",
432
+ "some_randome_field": "some_random_value",
433
+ "parent": None,
434
+ },
435
+ }
436
+
437
+ # AND a dataclass that references the parent context
438
+ class MyData(UniversalBaseModel):
439
+ foo: str
440
+ parent: ParentContext
441
+
442
+ # WHEN the data is deserialized
443
+ event = MyData.model_validate(data)
444
+
445
+ # THEN the event is deserialized correctly
446
+ assert event.parent
447
+ assert event.parent.type == "UNKNOWN"
448
+ assert event.parent.span_id == UUID("123e4567-e89b-12d3-a456-426614174000")
449
+ assert event.parent.parent is None
@@ -1,9 +1,10 @@
1
1
  from datetime import datetime
2
2
  import json
3
3
  from uuid import UUID, uuid4
4
- from typing import Annotated, Any, Dict, List, Literal, Optional, Union
4
+ from typing import Annotated, Any, Dict, List, Literal, Optional, Union, get_args
5
5
 
6
- from pydantic import BeforeValidator, Field
6
+ from pydantic import BeforeValidator, Field, GetCoreSchemaHandler, Tag, ValidationInfo
7
+ from pydantic_core import CoreSchema, core_schema
7
8
 
8
9
  from vellum.core.pydantic_utilities import UniversalBaseModel
9
10
  from vellum.workflows.state.encoder import DefaultStateEncoder
@@ -112,6 +113,59 @@ class APIRequestParentContext(BaseParentContext):
112
113
  type: Literal["API_REQUEST"] = "API_REQUEST"
113
114
 
114
115
 
116
+ class UnknownParentContext(BaseParentContext):
117
+ type: Literal["UNKNOWN"] = "UNKNOWN"
118
+
119
+
120
+ def _cast_parent_context_discriminator(v: Any) -> Any:
121
+ if v in PARENT_CONTEXT_TYPES:
122
+ return v
123
+
124
+ return "UNKNOWN"
125
+
126
+
127
+ def _get_parent_context_discriminator(v: Any) -> Any:
128
+ if isinstance(v, dict) and "type" in v:
129
+ return _cast_parent_context_discriminator(v["type"])
130
+
131
+ if isinstance(v, PARENT_CONTEXT_CHOICES):
132
+ return v.type
133
+
134
+ return _cast_parent_context_discriminator(v)
135
+
136
+
137
+ def _tag_parent_context_discriminator(v: Any) -> Any:
138
+ return Tag(_get_parent_context_discriminator(v))
139
+
140
+
141
+ def _validate_parent_context_discriminator(v: Any, info: ValidationInfo) -> Any:
142
+ if isinstance(v, str):
143
+ return _get_parent_context_discriminator(v)
144
+
145
+ if isinstance(v, dict) and "type" in v:
146
+ v["type"] = _get_parent_context_discriminator(v["type"])
147
+
148
+ return v
149
+
150
+
151
+ class ParentContextDiscriminator:
152
+ def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
153
+ original_schema = handler(source_type)
154
+ tagged_union_choices = {}
155
+ for index, choice in enumerate(original_schema["choices"]):
156
+ tagged_union_choices[Tag(PARENT_CONTEXT_TYPES[index])] = choice
157
+
158
+ tagged_union_schema = core_schema.tagged_union_schema(
159
+ tagged_union_choices,
160
+ _tag_parent_context_discriminator,
161
+ )
162
+ return core_schema.with_info_before_validator_function(
163
+ function=_validate_parent_context_discriminator,
164
+ schema=tagged_union_schema,
165
+ field_name="type",
166
+ )
167
+
168
+
115
169
  # Define the discriminated union
116
170
  ParentContext = Annotated[
117
171
  Union[
@@ -121,8 +175,13 @@ ParentContext = Annotated[
121
175
  PromptDeploymentParentContext,
122
176
  WorkflowSandboxParentContext,
123
177
  APIRequestParentContext,
178
+ UnknownParentContext,
124
179
  ],
125
- Field(discriminator="type"),
180
+ ParentContextDiscriminator(),
181
+ ]
182
+ PARENT_CONTEXT_CHOICES = get_args(get_args(ParentContext)[0])
183
+ PARENT_CONTEXT_TYPES = [
184
+ pc.model_fields["type"].default for pc in PARENT_CONTEXT_CHOICES if issubclass(pc, UniversalBaseModel)
126
185
  ]
127
186
 
128
187
  # Update the forward references
@@ -14,10 +14,11 @@ def read_file_from_path(node_filepath: str, script_filepath: str) -> Union[str,
14
14
  node_filepath_dir = os.path.dirname(node_filepath)
15
15
  full_filepath = os.path.join(node_filepath_dir, script_filepath)
16
16
 
17
- if os.path.isfile(full_filepath):
17
+ try:
18
18
  with open(full_filepath) as file:
19
19
  return file.read()
20
- return None
20
+ except (FileNotFoundError, IsADirectoryError):
21
+ return None
21
22
 
22
23
 
23
24
  class ListWrapper(list):
@@ -52,19 +52,18 @@ class GuardrailNode(BaseNode[StateType], Generic[StateType]):
52
52
  message="Metric execution must have one output named 'score' with type 'float'",
53
53
  code=WorkflowErrorCode.INVALID_OUTPUTS,
54
54
  )
55
-
56
- log = metric_outputs.get("log")
57
-
58
- if log is not None and not isinstance(log, str):
59
- raise NodeException(
60
- message="Metric execution log output must be of type 'str'",
61
- code=WorkflowErrorCode.INVALID_OUTPUTS,
62
- )
63
- if log:
64
- metric_outputs.pop("log")
65
-
66
55
  metric_outputs.pop("score")
67
56
 
57
+ if "log" in metric_outputs:
58
+ log = metric_outputs.pop("log") or ""
59
+ if not isinstance(log, str):
60
+ raise NodeException(
61
+ message="Metric execution log output must be of type 'str'",
62
+ code=WorkflowErrorCode.INVALID_OUTPUTS,
63
+ )
64
+ else:
65
+ log = None
66
+
68
67
  return self.Outputs(score=score, log=log, **metric_outputs)
69
68
 
70
69
  def _compile_metric_inputs(self) -> List[MetricDefinitionInput]:
@@ -0,0 +1,38 @@
1
+ import pytest
2
+
3
+ from vellum import TestSuiteRunMetricNumberOutput
4
+ from vellum.client.types.metric_definition_execution import MetricDefinitionExecution
5
+ from vellum.client.types.test_suite_run_metric_string_output import TestSuiteRunMetricStringOutput
6
+ from vellum.workflows.nodes.displayable.guardrail_node.node import GuardrailNode
7
+
8
+
9
+ @pytest.mark.parametrize("log_value", [None, ""], ids=["None", "Empty"])
10
+ def test_run_guardrail_node__empty_log(vellum_client, log_value):
11
+ """Confirm that we can successfully invoke a Guardrail Node"""
12
+
13
+ # GIVEN a Guardrail Node
14
+ class MyGuard(GuardrailNode):
15
+ metric_definition = "example_metric_definition"
16
+ metric_inputs = {}
17
+
18
+ # AND we know that the guardrail node will return a blank log
19
+ mock_metric_execution = MetricDefinitionExecution(
20
+ outputs=[
21
+ TestSuiteRunMetricNumberOutput(
22
+ name="score",
23
+ value=0.6,
24
+ ),
25
+ TestSuiteRunMetricStringOutput(
26
+ name="log",
27
+ value=log_value,
28
+ ),
29
+ ],
30
+ )
31
+ vellum_client.metric_definitions.execute_metric_definition.return_value = mock_metric_execution
32
+
33
+ # WHEN we run the Guardrail Node
34
+ outputs = MyGuard().run()
35
+
36
+ # THEN the workflow should have completed successfully
37
+ assert outputs.score == 0.6
38
+ assert outputs.log == ""
@@ -9,6 +9,7 @@ from httpx import Response
9
9
  from vellum.client.core.api_error import ApiError
10
10
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
11
11
  from vellum.client.types.chat_message import ChatMessage
12
+ from vellum.client.types.chat_message_prompt_block import ChatMessagePromptBlock
12
13
  from vellum.client.types.chat_message_request import ChatMessageRequest
13
14
  from vellum.client.types.execute_prompt_event import ExecutePromptEvent
14
15
  from vellum.client.types.fulfilled_execute_prompt_event import FulfilledExecutePromptEvent
@@ -241,7 +242,7 @@ def test_inline_prompt_node__parent_context(mock_httpx_transport, mock_complex_p
241
242
  # GIVEN a prompt node
242
243
  class MyNode(InlinePromptNode):
243
244
  ml_model = "gpt-4o"
244
- blocks = []
245
+ blocks = [ChatMessagePromptBlock(chat_role="USER", blocks=[])]
245
246
  prompt_inputs = {}
246
247
 
247
248
  # AND a known response from the httpx client
@@ -276,6 +277,16 @@ def test_inline_prompt_node__parent_context(mock_httpx_transport, mock_complex_p
276
277
 
277
278
  # AND the prompt is executed with the correct execution context
278
279
  call_request_args = mock_httpx_transport.handle_request.call_args_list[0][0][0]
279
- request_execution_context = json.loads(call_request_args.read().decode("utf-8"))["execution_context"]
280
+ call_request = json.loads(call_request_args.read().decode("utf-8"))
281
+ request_execution_context = call_request["execution_context"]
280
282
  assert request_execution_context["trace_id"] == str(trace_id)
281
283
  assert request_execution_context["parent_context"]
284
+
285
+ # AND the blocks are serialized as expected
286
+ assert call_request["blocks"] == [
287
+ {
288
+ "block_type": "CHAT_MESSAGE",
289
+ "chat_role": "USER",
290
+ "blocks": [],
291
+ }
292
+ ]
@@ -321,6 +321,7 @@ class WorkflowRunner(Generic[StateType]):
321
321
  )
322
322
  )
323
323
  except NodeException as e:
324
+ logger.info(e)
324
325
  self._workflow_event_inner_queue.put(
325
326
  NodeExecutionRejectedEvent(
326
327
  trace_id=node.state.meta.trace_id,
@@ -333,6 +334,7 @@ class WorkflowRunner(Generic[StateType]):
333
334
  )
334
335
  )
335
336
  except WorkflowInitializationException as e:
337
+ logger.info(e)
336
338
  self._workflow_event_inner_queue.put(
337
339
  NodeExecutionRejectedEvent(
338
340
  trace_id=node.state.meta.trace_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.26
3
+ Version: 0.14.28
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -41,7 +41,7 @@ vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=ybLIa4uclqVI
41
41
  vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=I1Jkp2htRINJATtv1e-zs9BrReFX842djpiVgBPHDYg,2186
42
42
  vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=BJ--Y-LCbGFJve3OFEKHVxrw8TKvgb342opYLJibToc,3128
43
43
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=aYZSJTxknU4LMiQdWk9LcK6CkhdozeDEMiRxfAyUNEc,2202
44
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=3fJzrFoGJX_igzWKgXp7f7-owdVBG6xuy-kBSWNbNxc,8734
44
+ vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=86hkneLIBS4Jel3GWsPVIIFqXGD3RHIpXw0iGa_Zues,8843
45
45
  vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=MU9I8CB1X1TgL1aa1eT6DHWwNJ-2v79t74xl0oy-fBo,5510
46
46
  vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=8CPnn06HIBxBOiECevUffeVmQmCpec6WtPQnNl9gj9Y,3748
47
47
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=xtyecs9mJ_WEwVpP12jxYwvehLXynhqLrPJ-Ahdk2GA,3232
@@ -54,7 +54,7 @@ vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=5EWzdA3TSUPlb
54
54
  vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py,sha256=p0fCvbKzpGvVrg67QmJd14m9E8_DG0u5s6SYIhzlkNA,4018
56
56
  vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=ulrpoYUW-5kIxfG4Lf5F2p0k_EoYKhmahEbF3P_eruM,1648
57
- vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=bg9INsXiWfyK047u8TD1oEOFYrqDq8GC7Hvgz69n7BE,1988
57
+ vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=fu9nxD4FInSfKbipJJ7UE617VkeUWs_uS6SeEz_8-Iw,4691
58
58
  vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=NuIw8Yb42KUdoGi3Ur8_7VPg50IC4hNrwAkCociwqNk,2091
59
59
  vellum_ee/workflows/display/nodes/vellum/tests/test_templating_node.py,sha256=Us32jf_FQnLuT4Bs2o5JyHxihCTAN8ozZghWIR0pl9k,3459
60
60
  vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py,sha256=mtzB8LJlFCHVFM4H5AanLp29gQfaVmnN4A4iaRGJHoI,2427
@@ -100,7 +100,7 @@ vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha25
100
100
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=nD6_lZnNp56siVJwhlWzSEHdMaSKjvWlsJa31SqfQAE,10623
101
101
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=AzGZ7ApiwXAHuymTJoXJketUegyC1dmB1blzoni5eh8,13423
102
102
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
- vellum_ee/workflows/server/virtual_file_loader.py,sha256=ET-Q83W5Cgqzqz3qtFNwtS2nJEIcm3VtvR5kffsT3VY,2262
103
+ vellum_ee/workflows/server/virtual_file_loader.py,sha256=7JphJcSO3H85qiC2DpFfBWjC3JjrbRmoynBC6KKHVsA,2710
104
104
  vellum_ee/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
105
  vellum_ee/workflows/tests/local_files/__init__.py,sha256=UyP6kKkRqr9cTKHQF4MVLdLk5MM9GGcLuqxXsQGm22Y,51
106
106
  vellum_ee/workflows/tests/local_files/base_class.py,sha256=UuiC7J68MVr6A4949QYiBpXOLdsvFG_Cw1muEPiHT6I,298
@@ -121,14 +121,14 @@ vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zi
121
121
  vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
122
122
  vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
123
123
  vellum_ee/workflows/tests/test_display_meta.py,sha256=C25dErwghPNXio49pvSRxyOuc96srH6eYEwTAWdE2zY,2258
124
- vellum_ee/workflows/tests/test_server.py,sha256=IWqjJXSbVFuV3w6b9lB3N6yoVAHJicmJuubASvw5QQM,2276
124
+ vellum_ee/workflows/tests/test_server.py,sha256=Ll4o9gg0Q4r8uX6Kt8LWgIz0u2zLwPiZxl3TuqoZpxg,4707
125
125
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
126
- vellum/__init__.py,sha256=NhD0OIit3WcTPXjWnLKErRlSzNN_K0WM8fWunk5SybE,40396
126
+ vellum/__init__.py,sha256=88-79I29hBTQvR1uH_BOCGMWuj2a4Nx82R_8KIESg28,40470
127
127
  vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
128
- vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
128
+ vellum/client/__init__.py,sha256=Jv9sI5BNFo2OYA9px_aREFSIp655ryC3eaZSRI6yH1k,117826
129
129
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
130
130
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
131
- vellum/client/core/client_wrapper.py,sha256=1wWo_Rb2vE2CNr--h4FyLmfr9mFgKq67hlfzKHAc2TE,1869
131
+ vellum/client/core/client_wrapper.py,sha256=UH9Mq4cJvem6l6xfFj0IWt_RPwlM-wLXpcS5F1LSo2k,1869
132
132
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
133
133
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
134
134
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -144,7 +144,7 @@ vellum/client/errors/bad_request_error.py,sha256=_EbO8mWqN9kFZPvIap8qa1lL_EWkRcs
144
144
  vellum/client/errors/forbidden_error.py,sha256=QO1kKlhClAPES6zsEK7g9pglWnxn3KWaOCAawWOg6Aw,263
145
145
  vellum/client/errors/internal_server_error.py,sha256=8USCagXyJJ1MOm9snpcXIUt6eNXvrd_aq7Gfcu1vlOI,268
146
146
  vellum/client/errors/not_found_error.py,sha256=tBVCeBC8n3C811WHRj_n-hs3h8MqwR5gp0vLiobk7W8,262
147
- vellum/client/resources/__init__.py,sha256=LzmnnOhM6oxD2Gt9cEotCkbVKywV_GH85-lEAERQG1o,1483
147
+ vellum/client/resources/__init__.py,sha256=UcVAa7Iwo6e5ijqrraBlDlUA5wnXYVfRMJwXGJkz8UM,1511
148
148
  vellum/client/resources/ad_hoc/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
149
149
  vellum/client/resources/ad_hoc/client.py,sha256=_liorv4AsoJ55kVu0a5oWB3Qeff0iUKXqoHEIyDWLxc,14173
150
150
  vellum/client/resources/container_images/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
@@ -159,7 +159,7 @@ vellum/client/resources/document_indexes/client.py,sha256=UcznU0NyvdNBpV4UCsTqG3
159
159
  vellum/client/resources/document_indexes/types/__init__.py,sha256=IoFqKHN_VBdEhC7VL8_6Jbatrn0e0zuYEJAJUahcUR0,196
160
160
  vellum/client/resources/document_indexes/types/document_indexes_list_request_status.py,sha256=sfUEB0cvOSmlE2iITqnMVyHv05Zy2fWP4QjCIYqMg0M,178
161
161
  vellum/client/resources/documents/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
162
- vellum/client/resources/documents/client.py,sha256=mSUvWf6iFXK6-QtfU9dTXNKhcRoMOA_6vyjtMzJ2S4g,27862
162
+ vellum/client/resources/documents/client.py,sha256=DOiZ0i0iuGxPWTxDZsd8kndZPMB4GslHvyj1gBkrTIc,26048
163
163
  vellum/client/resources/folder_entities/__init__.py,sha256=QOp7UMMB3a32GrfVaud35ECn4fqPBKXxCyClsDgd6GE,175
164
164
  vellum/client/resources/folder_entities/client.py,sha256=xkT6D1TwPxvf1eXgDhRpKg7_O2V78jwBsIGyJgnI5SY,11110
165
165
  vellum/client/resources/folder_entities/types/__init__.py,sha256=cHabrupjC-HL3kj-UZ9WdVzqHoQHCu6QsLFB3wlOs7k,212
@@ -170,6 +170,8 @@ vellum/client/resources/ml_models/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8
170
170
  vellum/client/resources/ml_models/client.py,sha256=XIYapTEY6GRNr7V0Kjy5bEeKmrhv9ul8qlQY2A5LFqQ,3872
171
171
  vellum/client/resources/organizations/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
172
172
  vellum/client/resources/organizations/client.py,sha256=Uye92moqjAcOCs4astmuFpT92QdC5SLMunA-C8_G-gA,3675
173
+ vellum/client/resources/prompts/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
174
+ vellum/client/resources/prompts/client.py,sha256=_rNTUjhl_ZF3vyQa_M1BSTrX4DlFXU_SXkwwCEYKD2s,6598
173
175
  vellum/client/resources/sandboxes/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
174
176
  vellum/client/resources/sandboxes/client.py,sha256=i-6DHap5k6gFcYS-kWI8ayJFVZxb-GENRft6BJwVam4,17158
175
177
  vellum/client/resources/test_suite_runs/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
@@ -191,7 +193,7 @@ vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCat
191
193
  vellum/client/resources/workspace_secrets/client.py,sha256=h7UzXLyTttPq1t-JZGMg1BWxypxJvBGUdqg7KGT7MK4,8027
192
194
  vellum/client/resources/workspaces/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
193
195
  vellum/client/resources/workspaces/client.py,sha256=RthwzN1o-Jxwg5yyNNodavFyNUSxfLoTv26w3mRR5g8,3595
194
- vellum/client/types/__init__.py,sha256=aiQpSDBi__mK4Lqi2kzc9Cp0LcrOI_TOHoq4FHK5zDE,61186
196
+ vellum/client/types/__init__.py,sha256=MUfv33R5OwoLX1dHVETCtWFNM3Xz-jWPJ2Z8ZrXlLqM,61259
195
197
  vellum/client/types/ad_hoc_execute_prompt_event.py,sha256=bCjujA2XsOgyF3bRZbcEqV2rOIymRgsLoIRtZpB14xg,607
196
198
  vellum/client/types/ad_hoc_expand_meta.py,sha256=1gv-NCsy_6xBYupLvZH979yf2VMdxAU-l0y0ynMKZaw,1331
197
199
  vellum/client/types/ad_hoc_fulfilled_prompt_execution_meta.py,sha256=Bfvf1d_dkmshxRACVM5vcxbH_7AQY23RmrrnPc0ytYY,939
@@ -488,6 +490,7 @@ vellum/client/types/prompt_block_state.py,sha256=BRAzTYARoSU36IVZGWMeeqhl5fgFMXC
488
490
  vellum/client/types/prompt_deployment_expand_meta_request.py,sha256=agsiAaHB6lDoZPlnfJ2nmhB4Ud4EiJJTX05YmduyCPo,1910
489
491
  vellum/client/types/prompt_deployment_input_request.py,sha256=KrT4-Ew2VvTWXEkYQz2oyHn5EDOgrMW7FzRFaPH3ARg,353
490
492
  vellum/client/types/prompt_deployment_parent_context.py,sha256=eu8dYmRb789uZeFVzbRkJrErDYZXo35f2qaNBcY0wOQ,2319
493
+ vellum/client/types/prompt_exec_config.py,sha256=kthvyEe-IzfTpOBd1fYrczHuxD-v7k6cwjmuaY170RQ,1390
491
494
  vellum/client/types/prompt_execution_meta.py,sha256=3hhMZgdAR5mKfnh2e_eVN3oKfT0E9w26khVPrpjn7jk,1141
492
495
  vellum/client/types/prompt_node_execution_meta.py,sha256=IyWH__nCp5uwS0N32b2ZEsA-Fv7AZDB4nnlRZayU2Gc,888
493
496
  vellum/client/types/prompt_node_result.py,sha256=3jewO-nPodoXTq_5RxgwhKfDZrvoPjRZ_vUXLeqiuHY,749
@@ -822,6 +825,8 @@ vellum/resources/ml_models/__init__.py,sha256=qIepoIEWDHz3u7i0bW3jnTpdTdfPGhA1LB
822
825
  vellum/resources/ml_models/client.py,sha256=RSYFEe1BnFTDBMur2_eR3ZkLZbdWeTGe_OIuMwcsfdw,158
823
826
  vellum/resources/organizations/__init__.py,sha256=FD1umjszsErkQIAI6aZ7Lv_T6iN5IafeCbgv25uIpYo,155
824
827
  vellum/resources/organizations/client.py,sha256=68HAX4pswpJDH0-Yjc3teoloSJBUGRv8O1V8tCqFSuk,162
828
+ vellum/resources/prompts/__init__.py,sha256=CtN_jI0nc0C3yqxUPR1uWs5Mvxhlce5c-d8E96GVt4g,149
829
+ vellum/resources/prompts/client.py,sha256=9S00NNuuiz41m6-kOL6KCxu9bnYMORrXrXVWfFEeW5o,156
825
830
  vellum/resources/sandboxes/__init__.py,sha256=sycp4Bgvj9GzBGjiXhtmKFjOdBsIoDfMFaQrvDK_lGo,151
826
831
  vellum/resources/sandboxes/client.py,sha256=PBpYOg43HN-9B4YKtPqmE1aFag39ypLc5UWSxixUJjo,158
827
832
  vellum/resources/test_suite_runs/__init__.py,sha256=PfRYjodfN_rYZlUTiBnVXxdwQNcdmI-qT6MCqubd3ug,157
@@ -1140,6 +1145,7 @@ vellum/types/prompt_block_state.py,sha256=tKqNrZnHWjvfGS_6oIUTpdCPGxvRJa31Le6qWL
1140
1145
  vellum/types/prompt_deployment_expand_meta_request.py,sha256=5dBdvjjK9zCKxrPMdKQPj6iG8A06GAlb_zazde6qZsU,175
1141
1146
  vellum/types/prompt_deployment_input_request.py,sha256=z8CxCZWnKW8BBZajQ6iDnz-2gaxU-FrnYrVe_MvC3FU,169
1142
1147
  vellum/types/prompt_deployment_parent_context.py,sha256=U9X9PvXhG6ZUE8RxLrH13xfqKvs3DOwbxzWmujoXTbg,170
1148
+ vellum/types/prompt_exec_config.py,sha256=aNeOGDi6l2rVzvkFt8CJE6L3W2EmY8gZaSb5051w8as,156
1143
1149
  vellum/types/prompt_execution_meta.py,sha256=_5izDjusf-TM69zKhvXr5EHH4Fx9jfWkg8F5_KNJV-w,159
1144
1150
  vellum/types/prompt_node_execution_meta.py,sha256=cJoHlIn_lb_sLpQniB8eszRJvFI6mJij9QgUIiKtiCY,164
1145
1151
  vellum/types/prompt_node_result.py,sha256=9ootTTh8lscQ-0WE0-bqdmn7XFvpP7uavO-g7mPkA3Q,156
@@ -1441,8 +1447,8 @@ vellum/workflows/errors/types.py,sha256=tVW7Il9zalnwWzdoDLqYPIvRTOhXIv6FPORZAbU7
1441
1447
  vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyuUK48Qw,759
1442
1448
  vellum/workflows/events/node.py,sha256=jbmNHjdp331Q1IRK-AWtAxwF6Lidb9R7__N5rQuilE8,5401
1443
1449
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1444
- vellum/workflows/events/tests/test_event.py,sha256=sHcKhZPDPtzZfTmehL4NORA_StR4M6nZDcx9kz3Avo0,16866
1445
- vellum/workflows/events/types.py,sha256=ArTnICVLE54ewwPi_YUX_JM_LZKdNb_qz0YI0lzP36I,3736
1450
+ vellum/workflows/events/tests/test_event.py,sha256=WRxjOO1470rFH40O56RWjhonIdupW782h_FRAhIQZCQ,17823
1451
+ vellum/workflows/events/types.py,sha256=cKXEZEZ4C_O38CH-qiu8nYSMy2DVJ66lQayJO5A-haU,5690
1446
1452
  vellum/workflows/events/workflow.py,sha256=xdqU6WOexaAqzJbU2Zw42o2LJhK7SDPtTFO5REGv17I,7293
1447
1453
  vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
1448
1454
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1542,7 +1548,7 @@ vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=
1542
1548
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1543
1549
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1544
1550
  vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=xAaoOfQHQUlp0iKlig87t0aT2cJM-8PxiTb1QDg8VmY,24641
1545
- vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=tBuR9cV1caFtpGuQrw_LcPDAfzLI-Mpmi1Klzpr34KM,3870
1551
+ vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=PI0IQysC3uASv4nof23O4gIWpoNl3tRleb1q417bfTw,3896
1546
1552
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1547
1553
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
1548
1554
  vellum/workflows/nodes/displayable/conftest.py,sha256=tD_WIiw5WjFqnzgnGLtEZDaMj2XhQ1DptnBTKYeBbI0,5705
@@ -1551,11 +1557,12 @@ vellum/workflows/nodes/displayable/final_output_node/node.py,sha256=PuQ0RvtAmoSI
1551
1557
  vellum/workflows/nodes/displayable/final_output_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1552
1558
  vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py,sha256=E6LQ74qZjY4Xi4avx2qdOCgGhF8pEcNLBh8cqYRkzMI,709
1553
1559
  vellum/workflows/nodes/displayable/guardrail_node/__init__.py,sha256=Ab5eXmOoBhyV4dMWdzh32HLUmnPIBEK_zFCT38C4Fng,68
1554
- vellum/workflows/nodes/displayable/guardrail_node/node.py,sha256=g6xG2-fEKGxYtJwbex1X5IodP3cJ7DqSthi80d2-Lo4,4395
1560
+ vellum/workflows/nodes/displayable/guardrail_node/node.py,sha256=YMXBLHB4_TYWGvbWMQP2WH0ckktK1uFDOEYkRJc-RfE,4422
1561
+ vellum/workflows/nodes/displayable/guardrail_node/test_node.py,sha256=1yPIAt4_GWiUKT6u3rTW2XKp62b8xG8Jj3JWeCm5ZDM,1368
1555
1562
  vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py,sha256=gSUOoEZLlrx35-tQhSAd3An8WDwBqyiQh-sIebLU9wU,74
1556
1563
  vellum/workflows/nodes/displayable/inline_prompt_node/node.py,sha256=8RXZqWMzViUjFfbpmcy1gkSsKnEpci8BGwsuPYv4xMQ,3380
1557
1564
  vellum/workflows/nodes/displayable/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1558
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=dVapol3P0NnbyUbn5ZBoRtUjt75yhoo0HYF5zkpokGA,10175
1565
+ vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=D06GymJGoMt9StKpTzqYk8wvmRedkvcRdYUHPUznvK4,10554
1559
1566
  vellum/workflows/nodes/displayable/merge_node/__init__.py,sha256=J8IC08dSH7P76wKlNuxe1sn7toNGtSQdFirUbtPDEs0,60
1560
1567
  vellum/workflows/nodes/displayable/merge_node/node.py,sha256=nZtGGVAvY4fvGg8vwV6sTQ8_QLRnigeXt0vf2FL272A,450
1561
1568
  vellum/workflows/nodes/displayable/note_node/__init__.py,sha256=KWA3P4fyYJ-fOTky8qNGlcOotQ-HeHJ9AjZt6mRQmCE,58
@@ -1607,7 +1614,7 @@ vellum/workflows/references/workflow_input.py,sha256=lq7BiiLBHQNP-vP2p1TN2QBq0_L
1607
1614
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1608
1615
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1609
1616
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1610
- vellum/workflows/runner/runner.py,sha256=pK_cRWS75x_sX7zrnltz3IykCTgoAWA7AW19Ih5BaB4,31759
1617
+ vellum/workflows/runner/runner.py,sha256=ww4fjZJBENkB5HJxdj92kTz7k_EyifCeAreupy5qIxs,31813
1611
1618
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1612
1619
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1613
1620
  vellum/workflows/state/base.py,sha256=Vkhneko3VlQrPsMLU1PYSzXU_W1u7_AraJsghiv5O-4,15512
@@ -1643,8 +1650,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1643
1650
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1644
1651
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=tCxrV3QBHL8wfdEO3bvKteDdw32xBlUl1_WxkAwaONw,8344
1645
1652
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1646
- vellum_ai-0.14.26.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1647
- vellum_ai-0.14.26.dist-info/METADATA,sha256=LajPvDTo4iXyXPY7rs0OTu7CXWL8oZHb9tb3c58XP5g,5484
1648
- vellum_ai-0.14.26.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1649
- vellum_ai-0.14.26.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1650
- vellum_ai-0.14.26.dist-info/RECORD,,
1653
+ vellum_ai-0.14.28.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1654
+ vellum_ai-0.14.28.dist-info/METADATA,sha256=N7nbaIH39gbgUyBqZttgkemMWEoUpoQUJLtpd6bxTl0,5484
1655
+ vellum_ai-0.14.28.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1656
+ vellum_ai-0.14.28.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1657
+ vellum_ai-0.14.28.dist-info/RECORD,,
@@ -94,7 +94,8 @@ class BaseInlinePromptNodeDisplay(BaseNodeVellumDisplay[_InlinePromptNodeType],
94
94
  input_name=variable_name,
95
95
  value=variable_value,
96
96
  display_context=display_context,
97
- input_id=self.node_input_ids_by_name.get(variable_name),
97
+ input_id=self.node_input_ids_by_name.get(f"{InlinePromptNode.prompt_inputs.name}.{variable_name}")
98
+ or self.node_input_ids_by_name.get(variable_name),
98
99
  )
99
100
  vellum_variable_type = infer_vellum_variable_type(variable_value)
100
101
  node_inputs.append(node_input)
@@ -1,7 +1,12 @@
1
+ import pytest
2
+ from uuid import UUID
3
+ from typing import Type
4
+
1
5
  from vellum.workflows import BaseWorkflow
2
6
  from vellum.workflows.nodes import BaseNode
3
7
  from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
4
8
  from vellum.workflows.references.lazy import LazyReference
9
+ from vellum_ee.workflows.display.nodes.vellum.inline_prompt_node import BaseInlinePromptNodeDisplay
5
10
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
6
11
  from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
7
12
 
@@ -34,7 +39,7 @@ def test_serialize_node__lazy_reference_in_prompt_inputs():
34
39
 
35
40
  assert lazy_reference_node["inputs"] == [
36
41
  {
37
- "id": "fba6a4d5-835a-4e99-afb7-f6a4aed15110",
42
+ "id": "aa81c1bc-d5d8-4ae8-8946-e9f4d0c1ab5f",
38
43
  "key": "attr",
39
44
  "value": {
40
45
  "combinator": "OR",
@@ -50,3 +55,77 @@ def test_serialize_node__lazy_reference_in_prompt_inputs():
50
55
  },
51
56
  }
52
57
  ]
58
+
59
+
60
+ def _no_display_class(Node: Type[InlinePromptNode]):
61
+ return None
62
+
63
+
64
+ def _display_class_with_node_input_ids_by_name(Node: Type[InlinePromptNode]):
65
+ class PromptNodeDisplay(BaseInlinePromptNodeDisplay[Node]): # type: ignore[valid-type]
66
+ node_input_ids_by_name = {"foo": UUID("fba6a4d5-835a-4e99-afb7-f6a4aed15110")}
67
+
68
+ return PromptNodeDisplay
69
+
70
+
71
+ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[InlinePromptNode]):
72
+ class PromptNodeDisplay(BaseInlinePromptNodeDisplay[Node]): # type: ignore[valid-type]
73
+ node_input_ids_by_name = {"prompt_inputs.foo": UUID("fba6a4d5-835a-4e99-afb7-f6a4aed15110")}
74
+
75
+ return PromptNodeDisplay
76
+
77
+
78
+ @pytest.mark.parametrize(
79
+ ["GetDisplayClass", "expected_input_id"],
80
+ [
81
+ (_no_display_class, "8aa4ce7f-5eb8-41b7-abd0-ea2b40c8fb88"),
82
+ (_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
83
+ (_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
84
+ ],
85
+ ids=[
86
+ "no_display_class",
87
+ "display_class_with_node_input_ids_by_name",
88
+ "display_class_with_node_input_ids_by_name_with_inputs_prefix",
89
+ ],
90
+ )
91
+ def test_serialize_node__prompt_inputs(GetDisplayClass, expected_input_id):
92
+ # GIVEN a prompt node with inputs
93
+ class MyPromptNode(InlinePromptNode):
94
+ prompt_inputs = {"foo": "bar"}
95
+ blocks = []
96
+ ml_model = "gpt-4o"
97
+
98
+ # AND a workflow with the prompt node
99
+ class Workflow(BaseWorkflow):
100
+ graph = MyPromptNode
101
+
102
+ # AND a display class
103
+ GetDisplayClass(MyPromptNode)
104
+
105
+ # WHEN the workflow is serialized
106
+ workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=Workflow)
107
+ serialized_workflow: dict = workflow_display.serialize()
108
+
109
+ # THEN the node should properly serialize the inputs
110
+ my_prompt_node = next(
111
+ node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["id"] == str(MyPromptNode.__id__)
112
+ )
113
+
114
+ assert my_prompt_node["inputs"] == [
115
+ {
116
+ "id": expected_input_id,
117
+ "key": "foo",
118
+ "value": {
119
+ "rules": [
120
+ {
121
+ "type": "CONSTANT_VALUE",
122
+ "data": {
123
+ "type": "STRING",
124
+ "value": "bar",
125
+ },
126
+ }
127
+ ],
128
+ "combinator": "OR",
129
+ },
130
+ }
131
+ ]
@@ -1,5 +1,7 @@
1
1
  import importlib
2
+ from importlib.machinery import ModuleSpec
2
3
  import re
4
+ import sys
3
5
  from typing import Optional
4
6
 
5
7
 
@@ -8,11 +10,20 @@ class VirtualFileLoader(importlib.abc.Loader):
8
10
  self.files = files
9
11
  self.namespace = namespace
10
12
 
11
- def create_module(self, spec):
12
- return None # use default module creation
13
+ def create_module(self, spec: ModuleSpec):
14
+ """
15
+ We started with cpython/Lib/importlib/_bootstrap.py::FrozenImporter::create_module here
16
+
17
+ https://github.com/python/cpython/blob/053c285f6b41f92fbdd1d4ff0c959cceefacd7cd/Lib/importlib/_bootstrap.py#L1160C1-L1169C22
18
+
19
+ and reduced our needs to just updating the __file__ attribute directly.
20
+ """
21
+ module = type(sys)(spec.name)
22
+ module.__file__ = spec.origin
23
+ return module
13
24
 
14
25
  def exec_module(self, module):
15
- module_info = self._resolve_module(module.__spec__.origin)
26
+ module_info = self._resolve_module(module.__spec__.name)
16
27
 
17
28
  if module_info:
18
29
  file_path, code = module_info
@@ -66,7 +77,8 @@ class VirtualFileFinder(importlib.abc.MetaPathFinder, importlib.abc.Loader):
66
77
  return importlib.machinery.ModuleSpec(
67
78
  fullname,
68
79
  self.loader,
69
- origin=fullname,
80
+ origin=file_path,
70
81
  is_package=is_package,
71
82
  )
83
+
72
84
  return None
@@ -1,13 +1,21 @@
1
+ import pytest
1
2
  import sys
2
3
  from uuid import uuid4
3
4
  from typing import Type, cast
4
5
 
5
6
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
7
+ from vellum.client.types.code_executor_response import CodeExecutorResponse
8
+ from vellum.client.types.number_vellum_value import NumberVellumValue
6
9
  from vellum.workflows import BaseWorkflow
7
10
  from vellum.workflows.nodes import BaseNode
8
11
  from vellum_ee.workflows.server.virtual_file_loader import VirtualFileFinder
9
12
 
10
13
 
14
+ @pytest.fixture
15
+ def mock_open(mocker):
16
+ return mocker.patch("vellum.workflows.nodes.displayable.code_execution_node.utils.open")
17
+
18
+
11
19
  def test_load_workflow_event_display_context():
12
20
  # DEPRECATED: Use `vellum.workflows.events.workflow.WorkflowEventDisplayContext` instead. Will be removed in 0.15.0
13
21
  from vellum_ee.workflows.display.types import WorkflowEventDisplayContext
@@ -69,3 +77,74 @@ class StartNode(BaseNode):
69
77
  # AND the lazy reference has the correct name
70
78
  assert start_node.foo.instance
71
79
  assert start_node.foo.instance.name == "StartNode.Outputs.bar"
80
+
81
+
82
+ def test_load_from_module__ts_code_in_file_loader(
83
+ mock_open,
84
+ vellum_client,
85
+ ):
86
+ # GIVEN typescript code
87
+ ts_code = """async function main(): any {
88
+ return 5;
89
+ }"""
90
+
91
+ # AND a workflow module with only a code execution node
92
+ files = {
93
+ "__init__.py": "",
94
+ "workflow.py": """\
95
+ from vellum.workflows import BaseWorkflow
96
+ from .nodes.code_execution_node import CodeExecutionNode
97
+
98
+ class Workflow(BaseWorkflow):
99
+ graph = CodeExecutionNode
100
+ """,
101
+ "nodes/__init__.py": """\
102
+ from .code_execution_node import CodeExecutionNode
103
+
104
+ __all__ = ["CodeExecutionNode"]
105
+ """,
106
+ "nodes/code_execution_node.py": """\
107
+ from typing import Any
108
+
109
+ from vellum.workflows.nodes.displayable import CodeExecutionNode as BaseCodeExecutionNode
110
+ from vellum.workflows.state import BaseState
111
+
112
+ class CodeExecutionNode(BaseCodeExecutionNode[BaseState, int]):
113
+ filepath = "./script.ts"
114
+ code_inputs = {}
115
+ runtime = "TYPESCRIPT_5_3_3"
116
+ packages = []
117
+ """,
118
+ "nodes/code_execution_node/script.ts": ts_code,
119
+ }
120
+
121
+ namespace = str(uuid4())
122
+
123
+ # AND the virtual file loader is registered
124
+ sys.meta_path.append(VirtualFileFinder(files, namespace))
125
+
126
+ # AND the open function returns our file content
127
+ mock_open.return_value.__enter__.return_value.read.return_value = ts_code
128
+
129
+ # AND we know what the Code Execution Node will respond with
130
+ mock_code_execution = CodeExecutorResponse(
131
+ log="hello",
132
+ output=NumberVellumValue(value=5),
133
+ )
134
+ vellum_client.execute_code.return_value = mock_code_execution
135
+
136
+ # WHEN the workflow is loaded
137
+ Workflow = BaseWorkflow.load_from_module(namespace)
138
+ workflow = Workflow()
139
+
140
+ # THEN the workflow is successfully initialized
141
+ assert workflow
142
+
143
+ event = workflow.run()
144
+ assert event.name == "workflow.execution.fulfilled", event.model_dump_json()
145
+
146
+ # AND we pass in the correct file path to the open function
147
+ assert mock_open.call_args[0][0] == f"{namespace}/nodes/./script.ts"
148
+
149
+ # AND we invoke the Code Execution Node with the correct code
150
+ assert vellum_client.execute_code.call_args.kwargs["code"] == ts_code