vellum-ai 0.12.13__py3-none-any.whl → 0.12.15__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.
- vellum/__init__.py +9 -0
- vellum/client/__init__.py +2 -6
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/environment.py +3 -3
- vellum/client/resources/ad_hoc/client.py +2 -6
- vellum/client/resources/container_images/client.py +0 -8
- vellum/client/resources/metric_definitions/client.py +2 -6
- vellum/client/resources/workflows/client.py +8 -8
- vellum/client/types/__init__.py +6 -0
- vellum/client/types/audio_prompt_block.py +29 -0
- vellum/client/types/function_call_prompt_block.py +30 -0
- vellum/client/types/image_prompt_block.py +29 -0
- vellum/client/types/prompt_block.py +12 -1
- vellum/client/types/workflow_push_response.py +1 -0
- vellum/plugins/pydantic.py +12 -2
- vellum/types/audio_prompt_block.py +3 -0
- vellum/types/function_call_prompt_block.py +3 -0
- vellum/types/image_prompt_block.py +3 -0
- vellum/workflows/descriptors/tests/test_utils.py +3 -0
- vellum/workflows/nodes/bases/base.py +4 -1
- vellum/workflows/nodes/bases/base_adornment_node.py +75 -0
- vellum/workflows/nodes/bases/tests/test_base_node.py +13 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +2 -0
- vellum/workflows/nodes/core/map_node/node.py +49 -45
- vellum/workflows/nodes/core/retry_node/node.py +10 -45
- vellum/workflows/nodes/core/try_node/node.py +12 -84
- vellum/workflows/nodes/utils.py +44 -1
- vellum/workflows/references/constant.py +21 -0
- vellum/workflows/runner/runner.py +4 -3
- vellum/workflows/types/cycle_map.py +34 -0
- vellum/workflows/workflows/base.py +4 -11
- {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/METADATA +2 -2
- {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/RECORD +52 -39
- vellum_cli/config.py +4 -0
- vellum_cli/pull.py +20 -5
- vellum_cli/push.py +7 -0
- vellum_cli/tests/test_pull.py +19 -1
- vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/base_node.py +18 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +10 -41
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +4 -14
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +174 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +2 -10
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +2 -10
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +5 -19
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +2 -8
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +14 -25
- vellum_ee/workflows/server/__init__.py +0 -0
- vellum_ee/workflows/server/virtual_file_loader.py +42 -0
- {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/LICENSE +0 -0
- {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/WHEEL +0 -0
- {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/entry_points.txt +0 -0
vellum/__init__.py
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# This file was auto-generated by Fern from our API Definition.
|
2
|
+
from .plugins.utils import load_runtime_plugins
|
3
|
+
|
4
|
+
load_runtime_plugins()
|
2
5
|
|
3
6
|
from .types import (
|
4
7
|
AdHocExecutePromptEvent,
|
@@ -21,6 +24,7 @@ from .types import (
|
|
21
24
|
ArrayVellumValueRequest,
|
22
25
|
AudioChatMessageContent,
|
23
26
|
AudioChatMessageContentRequest,
|
27
|
+
AudioPromptBlock,
|
24
28
|
AudioVariableValue,
|
25
29
|
AudioVellumValue,
|
26
30
|
AudioVellumValueRequest,
|
@@ -131,6 +135,7 @@ from .types import (
|
|
131
135
|
FunctionCallChatMessageContentValue,
|
132
136
|
FunctionCallChatMessageContentValueRequest,
|
133
137
|
FunctionCallInput,
|
138
|
+
FunctionCallPromptBlock,
|
134
139
|
FunctionCallRequest,
|
135
140
|
FunctionCallVariableValue,
|
136
141
|
FunctionCallVellumValue,
|
@@ -155,6 +160,7 @@ from .types import (
|
|
155
160
|
HkunlpInstructorXlVectorizerRequest,
|
156
161
|
ImageChatMessageContent,
|
157
162
|
ImageChatMessageContentRequest,
|
163
|
+
ImagePromptBlock,
|
158
164
|
ImageVariableValue,
|
159
165
|
ImageVellumValue,
|
160
166
|
ImageVellumValueRequest,
|
@@ -545,6 +551,7 @@ __all__ = [
|
|
545
551
|
"AsyncVellum",
|
546
552
|
"AudioChatMessageContent",
|
547
553
|
"AudioChatMessageContentRequest",
|
554
|
+
"AudioPromptBlock",
|
548
555
|
"AudioVariableValue",
|
549
556
|
"AudioVellumValue",
|
550
557
|
"AudioVellumValueRequest",
|
@@ -660,6 +667,7 @@ __all__ = [
|
|
660
667
|
"FunctionCallChatMessageContentValue",
|
661
668
|
"FunctionCallChatMessageContentValueRequest",
|
662
669
|
"FunctionCallInput",
|
670
|
+
"FunctionCallPromptBlock",
|
663
671
|
"FunctionCallRequest",
|
664
672
|
"FunctionCallVariableValue",
|
665
673
|
"FunctionCallVellumValue",
|
@@ -684,6 +692,7 @@ __all__ = [
|
|
684
692
|
"HkunlpInstructorXlVectorizerRequest",
|
685
693
|
"ImageChatMessageContent",
|
686
694
|
"ImageChatMessageContentRequest",
|
695
|
+
"ImagePromptBlock",
|
687
696
|
"ImageVariableValue",
|
688
697
|
"ImageVellumValue",
|
689
698
|
"ImageVellumValueRequest",
|
vellum/client/__init__.py
CHANGED
@@ -154,8 +154,6 @@ class Vellum:
|
|
154
154
|
request_options: typing.Optional[RequestOptions] = None,
|
155
155
|
) -> CodeExecutorResponse:
|
156
156
|
"""
|
157
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
158
|
-
|
159
157
|
Parameters
|
160
158
|
----------
|
161
159
|
code : str
|
@@ -203,7 +201,7 @@ class Vellum:
|
|
203
201
|
"""
|
204
202
|
_response = self._client_wrapper.httpx_client.request(
|
205
203
|
"v1/execute-code",
|
206
|
-
base_url=self._client_wrapper.get_environment().
|
204
|
+
base_url=self._client_wrapper.get_environment().predict,
|
207
205
|
method="POST",
|
208
206
|
json={
|
209
207
|
"code": code,
|
@@ -1419,8 +1417,6 @@ class AsyncVellum:
|
|
1419
1417
|
request_options: typing.Optional[RequestOptions] = None,
|
1420
1418
|
) -> CodeExecutorResponse:
|
1421
1419
|
"""
|
1422
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
1423
|
-
|
1424
1420
|
Parameters
|
1425
1421
|
----------
|
1426
1422
|
code : str
|
@@ -1476,7 +1472,7 @@ class AsyncVellum:
|
|
1476
1472
|
"""
|
1477
1473
|
_response = await self._client_wrapper.httpx_client.request(
|
1478
1474
|
"v1/execute-code",
|
1479
|
-
base_url=self._client_wrapper.get_environment().
|
1475
|
+
base_url=self._client_wrapper.get_environment().predict,
|
1480
1476
|
method="POST",
|
1481
1477
|
json={
|
1482
1478
|
"code": code,
|
@@ -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.12.
|
21
|
+
"X-Fern-SDK-Version": "0.12.15",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
vellum/client/environment.py
CHANGED
@@ -6,12 +6,12 @@ from __future__ import annotations
|
|
6
6
|
class VellumEnvironment:
|
7
7
|
PRODUCTION: VellumEnvironment
|
8
8
|
|
9
|
-
def __init__(self, *, default: str,
|
9
|
+
def __init__(self, *, default: str, predict: str, documents: str):
|
10
10
|
self.default = default
|
11
|
-
self.documents = documents
|
12
11
|
self.predict = predict
|
12
|
+
self.documents = documents
|
13
13
|
|
14
14
|
|
15
15
|
VellumEnvironment.PRODUCTION = VellumEnvironment(
|
16
|
-
default="https://api.vellum.ai",
|
16
|
+
default="https://api.vellum.ai", predict="https://predict.vellum.ai", documents="https://documents.vellum.ai"
|
17
17
|
)
|
@@ -43,8 +43,6 @@ class AdHocClient:
|
|
43
43
|
request_options: typing.Optional[RequestOptions] = None,
|
44
44
|
) -> typing.Iterator[AdHocExecutePromptEvent]:
|
45
45
|
"""
|
46
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
47
|
-
|
48
46
|
Parameters
|
49
47
|
----------
|
50
48
|
ml_model : str
|
@@ -111,7 +109,7 @@ class AdHocClient:
|
|
111
109
|
"""
|
112
110
|
with self._client_wrapper.httpx_client.stream(
|
113
111
|
"v1/ad-hoc/execute-prompt-stream",
|
114
|
-
base_url=self._client_wrapper.get_environment().
|
112
|
+
base_url=self._client_wrapper.get_environment().predict,
|
115
113
|
method="POST",
|
116
114
|
json={
|
117
115
|
"ml_model": ml_model,
|
@@ -211,8 +209,6 @@ class AsyncAdHocClient:
|
|
211
209
|
request_options: typing.Optional[RequestOptions] = None,
|
212
210
|
) -> typing.AsyncIterator[AdHocExecutePromptEvent]:
|
213
211
|
"""
|
214
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
215
|
-
|
216
212
|
Parameters
|
217
213
|
----------
|
218
214
|
ml_model : str
|
@@ -287,7 +283,7 @@ class AsyncAdHocClient:
|
|
287
283
|
"""
|
288
284
|
async with self._client_wrapper.httpx_client.stream(
|
289
285
|
"v1/ad-hoc/execute-prompt-stream",
|
290
|
-
base_url=self._client_wrapper.get_environment().
|
286
|
+
base_url=self._client_wrapper.get_environment().predict,
|
291
287
|
method="POST",
|
292
288
|
json={
|
293
289
|
"ml_model": ml_model,
|
@@ -134,8 +134,6 @@ class ContainerImagesClient:
|
|
134
134
|
|
135
135
|
def docker_service_token(self, *, request_options: typing.Optional[RequestOptions] = None) -> DockerServiceToken:
|
136
136
|
"""
|
137
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
138
|
-
|
139
137
|
Parameters
|
140
138
|
----------
|
141
139
|
request_options : typing.Optional[RequestOptions]
|
@@ -184,8 +182,6 @@ class ContainerImagesClient:
|
|
184
182
|
request_options: typing.Optional[RequestOptions] = None,
|
185
183
|
) -> ContainerImageRead:
|
186
184
|
"""
|
187
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
188
|
-
|
189
185
|
Parameters
|
190
186
|
----------
|
191
187
|
name : str
|
@@ -378,8 +374,6 @@ class AsyncContainerImagesClient:
|
|
378
374
|
self, *, request_options: typing.Optional[RequestOptions] = None
|
379
375
|
) -> DockerServiceToken:
|
380
376
|
"""
|
381
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
382
|
-
|
383
377
|
Parameters
|
384
378
|
----------
|
385
379
|
request_options : typing.Optional[RequestOptions]
|
@@ -436,8 +430,6 @@ class AsyncContainerImagesClient:
|
|
436
430
|
request_options: typing.Optional[RequestOptions] = None,
|
437
431
|
) -> ContainerImageRead:
|
438
432
|
"""
|
439
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
440
|
-
|
441
433
|
Parameters
|
442
434
|
----------
|
443
435
|
name : str
|
@@ -30,8 +30,6 @@ class MetricDefinitionsClient:
|
|
30
30
|
request_options: typing.Optional[RequestOptions] = None,
|
31
31
|
) -> MetricDefinitionExecution:
|
32
32
|
"""
|
33
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
34
|
-
|
35
33
|
Parameters
|
36
34
|
----------
|
37
35
|
id : str
|
@@ -68,7 +66,7 @@ class MetricDefinitionsClient:
|
|
68
66
|
"""
|
69
67
|
_response = self._client_wrapper.httpx_client.request(
|
70
68
|
f"v1/metric-definitions/{jsonable_encoder(id)}/execute",
|
71
|
-
base_url=self._client_wrapper.get_environment().
|
69
|
+
base_url=self._client_wrapper.get_environment().predict,
|
72
70
|
method="POST",
|
73
71
|
json={
|
74
72
|
"inputs": convert_and_respect_annotation_metadata(
|
@@ -159,8 +157,6 @@ class AsyncMetricDefinitionsClient:
|
|
159
157
|
request_options: typing.Optional[RequestOptions] = None,
|
160
158
|
) -> MetricDefinitionExecution:
|
161
159
|
"""
|
162
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
163
|
-
|
164
160
|
Parameters
|
165
161
|
----------
|
166
162
|
id : str
|
@@ -205,7 +201,7 @@ class AsyncMetricDefinitionsClient:
|
|
205
201
|
"""
|
206
202
|
_response = await self._client_wrapper.httpx_client.request(
|
207
203
|
f"v1/metric-definitions/{jsonable_encoder(id)}/execute",
|
208
|
-
base_url=self._client_wrapper.get_environment().
|
204
|
+
base_url=self._client_wrapper.get_environment().predict,
|
209
205
|
method="POST",
|
210
206
|
json={
|
211
207
|
"inputs": convert_and_respect_annotation_metadata(
|
@@ -35,8 +35,6 @@ class WorkflowsClient:
|
|
35
35
|
request_options: typing.Optional[RequestOptions] = None,
|
36
36
|
) -> typing.Iterator[bytes]:
|
37
37
|
"""
|
38
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
39
|
-
|
40
38
|
Parameters
|
41
39
|
----------
|
42
40
|
id : str
|
@@ -102,11 +100,10 @@ class WorkflowsClient:
|
|
102
100
|
workflow_sandbox_id: typing.Optional[str] = OMIT,
|
103
101
|
deployment_config: typing.Optional[WorkflowPushDeploymentConfigRequest] = OMIT,
|
104
102
|
artifact: typing.Optional[core.File] = OMIT,
|
103
|
+
dry_run: typing.Optional[bool] = OMIT,
|
105
104
|
request_options: typing.Optional[RequestOptions] = None,
|
106
105
|
) -> WorkflowPushResponse:
|
107
106
|
"""
|
108
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
109
|
-
|
110
107
|
Parameters
|
111
108
|
----------
|
112
109
|
exec_config : WorkflowPushExecConfig
|
@@ -121,6 +118,8 @@ class WorkflowsClient:
|
|
121
118
|
artifact : typing.Optional[core.File]
|
122
119
|
See core.File for more documentation
|
123
120
|
|
121
|
+
dry_run : typing.Optional[bool]
|
122
|
+
|
124
123
|
request_options : typing.Optional[RequestOptions]
|
125
124
|
Request-specific configuration.
|
126
125
|
|
@@ -150,6 +149,7 @@ class WorkflowsClient:
|
|
150
149
|
"label": label,
|
151
150
|
"workflow_sandbox_id": workflow_sandbox_id,
|
152
151
|
"deployment_config": deployment_config,
|
152
|
+
"dry_run": dry_run,
|
153
153
|
},
|
154
154
|
files={
|
155
155
|
"artifact": artifact,
|
@@ -188,8 +188,6 @@ class AsyncWorkflowsClient:
|
|
188
188
|
request_options: typing.Optional[RequestOptions] = None,
|
189
189
|
) -> typing.AsyncIterator[bytes]:
|
190
190
|
"""
|
191
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
192
|
-
|
193
191
|
Parameters
|
194
192
|
----------
|
195
193
|
id : str
|
@@ -255,11 +253,10 @@ class AsyncWorkflowsClient:
|
|
255
253
|
workflow_sandbox_id: typing.Optional[str] = OMIT,
|
256
254
|
deployment_config: typing.Optional[WorkflowPushDeploymentConfigRequest] = OMIT,
|
257
255
|
artifact: typing.Optional[core.File] = OMIT,
|
256
|
+
dry_run: typing.Optional[bool] = OMIT,
|
258
257
|
request_options: typing.Optional[RequestOptions] = None,
|
259
258
|
) -> WorkflowPushResponse:
|
260
259
|
"""
|
261
|
-
An internal-only endpoint that's subject to breaking changes without notice. Not intended for public use.
|
262
|
-
|
263
260
|
Parameters
|
264
261
|
----------
|
265
262
|
exec_config : WorkflowPushExecConfig
|
@@ -274,6 +271,8 @@ class AsyncWorkflowsClient:
|
|
274
271
|
artifact : typing.Optional[core.File]
|
275
272
|
See core.File for more documentation
|
276
273
|
|
274
|
+
dry_run : typing.Optional[bool]
|
275
|
+
|
277
276
|
request_options : typing.Optional[RequestOptions]
|
278
277
|
Request-specific configuration.
|
279
278
|
|
@@ -311,6 +310,7 @@ class AsyncWorkflowsClient:
|
|
311
310
|
"label": label,
|
312
311
|
"workflow_sandbox_id": workflow_sandbox_id,
|
313
312
|
"deployment_config": deployment_config,
|
313
|
+
"dry_run": dry_run,
|
314
314
|
},
|
315
315
|
files={
|
316
316
|
"artifact": artifact,
|
vellum/client/types/__init__.py
CHANGED
@@ -20,6 +20,7 @@ from .array_vellum_value import ArrayVellumValue
|
|
20
20
|
from .array_vellum_value_request import ArrayVellumValueRequest
|
21
21
|
from .audio_chat_message_content import AudioChatMessageContent
|
22
22
|
from .audio_chat_message_content_request import AudioChatMessageContentRequest
|
23
|
+
from .audio_prompt_block import AudioPromptBlock
|
23
24
|
from .audio_variable_value import AudioVariableValue
|
24
25
|
from .audio_vellum_value import AudioVellumValue
|
25
26
|
from .audio_vellum_value_request import AudioVellumValueRequest
|
@@ -138,6 +139,7 @@ from .function_call_chat_message_content_request import FunctionCallChatMessageC
|
|
138
139
|
from .function_call_chat_message_content_value import FunctionCallChatMessageContentValue
|
139
140
|
from .function_call_chat_message_content_value_request import FunctionCallChatMessageContentValueRequest
|
140
141
|
from .function_call_input import FunctionCallInput
|
142
|
+
from .function_call_prompt_block import FunctionCallPromptBlock
|
141
143
|
from .function_call_request import FunctionCallRequest
|
142
144
|
from .function_call_variable_value import FunctionCallVariableValue
|
143
145
|
from .function_call_vellum_value import FunctionCallVellumValue
|
@@ -166,6 +168,7 @@ from .hkunlp_instructor_xl_vectorizer import HkunlpInstructorXlVectorizer
|
|
166
168
|
from .hkunlp_instructor_xl_vectorizer_request import HkunlpInstructorXlVectorizerRequest
|
167
169
|
from .image_chat_message_content import ImageChatMessageContent
|
168
170
|
from .image_chat_message_content_request import ImageChatMessageContentRequest
|
171
|
+
from .image_prompt_block import ImagePromptBlock
|
169
172
|
from .image_variable_value import ImageVariableValue
|
170
173
|
from .image_vellum_value import ImageVellumValue
|
171
174
|
from .image_vellum_value_request import ImageVellumValueRequest
|
@@ -542,6 +545,7 @@ __all__ = [
|
|
542
545
|
"ArrayVellumValueRequest",
|
543
546
|
"AudioChatMessageContent",
|
544
547
|
"AudioChatMessageContentRequest",
|
548
|
+
"AudioPromptBlock",
|
545
549
|
"AudioVariableValue",
|
546
550
|
"AudioVellumValue",
|
547
551
|
"AudioVellumValueRequest",
|
@@ -652,6 +656,7 @@ __all__ = [
|
|
652
656
|
"FunctionCallChatMessageContentValue",
|
653
657
|
"FunctionCallChatMessageContentValueRequest",
|
654
658
|
"FunctionCallInput",
|
659
|
+
"FunctionCallPromptBlock",
|
655
660
|
"FunctionCallRequest",
|
656
661
|
"FunctionCallVariableValue",
|
657
662
|
"FunctionCallVellumValue",
|
@@ -676,6 +681,7 @@ __all__ = [
|
|
676
681
|
"HkunlpInstructorXlVectorizerRequest",
|
677
682
|
"ImageChatMessageContent",
|
678
683
|
"ImageChatMessageContentRequest",
|
684
|
+
"ImagePromptBlock",
|
679
685
|
"ImageVariableValue",
|
680
686
|
"ImageVellumValue",
|
681
687
|
"ImageVellumValueRequest",
|
@@ -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 AudioPromptBlock(UniversalBaseModel):
|
12
|
+
"""
|
13
|
+
A block that represents an audio file in a prompt template.
|
14
|
+
"""
|
15
|
+
|
16
|
+
block_type: typing.Literal["AUDIO"] = "AUDIO"
|
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
|
@@ -0,0 +1,30 @@
|
|
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 FunctionCallPromptBlock(UniversalBaseModel):
|
12
|
+
"""
|
13
|
+
A block that represents a function call in a prompt template.
|
14
|
+
"""
|
15
|
+
|
16
|
+
block_type: typing.Literal["FUNCTION_CALL"] = "FUNCTION_CALL"
|
17
|
+
state: typing.Optional[PromptBlockState] = None
|
18
|
+
cache_config: typing.Optional[EphemeralPromptCacheConfig] = None
|
19
|
+
id: typing.Optional[str] = None
|
20
|
+
name: str
|
21
|
+
arguments: typing.Dict[str, typing.Optional[typing.Any]]
|
22
|
+
|
23
|
+
if IS_PYDANTIC_V2:
|
24
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
25
|
+
else:
|
26
|
+
|
27
|
+
class Config:
|
28
|
+
frozen = True
|
29
|
+
smart_union = True
|
30
|
+
extra = pydantic.Extra.allow
|
@@ -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 ImagePromptBlock(UniversalBaseModel):
|
12
|
+
"""
|
13
|
+
A block that represents an image in a prompt template.
|
14
|
+
"""
|
15
|
+
|
16
|
+
block_type: typing.Literal["IMAGE"] = "IMAGE"
|
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
|
@@ -5,8 +5,19 @@ import typing
|
|
5
5
|
from .jinja_prompt_block import JinjaPromptBlock
|
6
6
|
from .variable_prompt_block import VariablePromptBlock
|
7
7
|
from .rich_text_prompt_block import RichTextPromptBlock
|
8
|
+
from .audio_prompt_block import AudioPromptBlock
|
9
|
+
from .function_call_prompt_block import FunctionCallPromptBlock
|
10
|
+
from .image_prompt_block import ImagePromptBlock
|
8
11
|
import typing
|
9
12
|
|
10
13
|
if typing.TYPE_CHECKING:
|
11
14
|
from .chat_message_prompt_block import ChatMessagePromptBlock
|
12
|
-
PromptBlock = typing.Union[
|
15
|
+
PromptBlock = typing.Union[
|
16
|
+
JinjaPromptBlock,
|
17
|
+
"ChatMessagePromptBlock",
|
18
|
+
VariablePromptBlock,
|
19
|
+
RichTextPromptBlock,
|
20
|
+
AudioPromptBlock,
|
21
|
+
FunctionCallPromptBlock,
|
22
|
+
ImagePromptBlock,
|
23
|
+
]
|
@@ -9,6 +9,7 @@ import pydantic
|
|
9
9
|
class WorkflowPushResponse(UniversalBaseModel):
|
10
10
|
workflow_sandbox_id: str
|
11
11
|
workflow_deployment_id: typing.Optional[str] = None
|
12
|
+
proposed_diffs: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
|
12
13
|
|
13
14
|
if IS_PYDANTIC_V2:
|
14
15
|
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
vellum/plugins/pydantic.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from functools import lru_cache
|
1
2
|
from typing import Any, Dict, Literal, Optional, Tuple, Union
|
2
3
|
|
3
4
|
from pydantic.plugin import (
|
@@ -10,12 +11,20 @@ from pydantic.plugin import (
|
|
10
11
|
)
|
11
12
|
from pydantic_core import CoreSchema
|
12
13
|
|
13
|
-
|
14
|
+
|
15
|
+
@lru_cache(maxsize=1)
|
16
|
+
def import_base_descriptor():
|
17
|
+
"""
|
18
|
+
We have to avoid importing from vellum.* in this file because it will cause a circular import.
|
19
|
+
"""
|
20
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
21
|
+
|
22
|
+
return BaseDescriptor
|
14
23
|
|
15
24
|
|
16
25
|
# https://docs.pydantic.dev/2.8/concepts/plugins/#build-a-plugin
|
17
26
|
class OnValidatePython(ValidatePythonHandlerProtocol):
|
18
|
-
tracked_descriptors: Dict[str,
|
27
|
+
tracked_descriptors: Dict[str, Any] = {}
|
19
28
|
|
20
29
|
def on_enter(
|
21
30
|
self,
|
@@ -31,6 +40,7 @@ class OnValidatePython(ValidatePythonHandlerProtocol):
|
|
31
40
|
return
|
32
41
|
|
33
42
|
self.tracked_descriptors = {}
|
43
|
+
BaseDescriptor = import_base_descriptor()
|
34
44
|
|
35
45
|
for key, value in input.items():
|
36
46
|
if isinstance(value, BaseDescriptor):
|
@@ -2,6 +2,7 @@ import pytest
|
|
2
2
|
|
3
3
|
from vellum.workflows.descriptors.utils import resolve_value
|
4
4
|
from vellum.workflows.nodes.bases.base import BaseNode
|
5
|
+
from vellum.workflows.references.constant import ConstantValueReference
|
5
6
|
from vellum.workflows.state.base import BaseState
|
6
7
|
|
7
8
|
|
@@ -73,6 +74,7 @@ class DummyNode(BaseNode[FixtureState]):
|
|
73
74
|
True,
|
74
75
|
),
|
75
76
|
(FixtureState.zeta["foo"], "bar"),
|
77
|
+
(ConstantValueReference(1), 1),
|
76
78
|
],
|
77
79
|
ids=[
|
78
80
|
"or",
|
@@ -116,6 +118,7 @@ class DummyNode(BaseNode[FixtureState]):
|
|
116
118
|
"is_not_blank",
|
117
119
|
"or_and",
|
118
120
|
"accessor",
|
121
|
+
"constants",
|
119
122
|
],
|
120
123
|
)
|
121
124
|
def test_resolve_value__happy_path(descriptor, expected_value):
|
@@ -214,6 +214,9 @@ class _BaseNodeExecutionMeta(type):
|
|
214
214
|
return self_execution_class.node_class.__name__ == other_execution_class.node_class.__name__
|
215
215
|
|
216
216
|
|
217
|
+
NodeRunResponse = Union[BaseOutputs, Iterator[BaseOutput]]
|
218
|
+
|
219
|
+
|
217
220
|
class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
|
218
221
|
__id__: UUID = uuid4_from_hash(__qualname__)
|
219
222
|
state: StateType
|
@@ -350,7 +353,7 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
|
|
350
353
|
|
351
354
|
self._inputs = MappingProxyType(all_inputs)
|
352
355
|
|
353
|
-
def run(self) ->
|
356
|
+
def run(self) -> NodeRunResponse:
|
354
357
|
return self.Outputs()
|
355
358
|
|
356
359
|
def __repr__(self) -> str:
|
@@ -0,0 +1,75 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, Generic, Optional, Tuple, Type
|
2
|
+
|
3
|
+
from vellum.workflows.nodes.bases.base import BaseNode, BaseNodeMeta
|
4
|
+
from vellum.workflows.outputs.base import BaseOutputs
|
5
|
+
from vellum.workflows.references.output import OutputReference
|
6
|
+
from vellum.workflows.types.generics import StateType
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from vellum.workflows import BaseWorkflow
|
10
|
+
|
11
|
+
|
12
|
+
class _BaseAdornmentNodeMeta(BaseNodeMeta):
|
13
|
+
def __new__(cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
|
14
|
+
node_class = super().__new__(cls, name, bases, dct)
|
15
|
+
|
16
|
+
subworkflow_attribute = dct.get("subworkflow")
|
17
|
+
if not subworkflow_attribute:
|
18
|
+
return node_class
|
19
|
+
|
20
|
+
if not issubclass(node_class, BaseAdornmentNode):
|
21
|
+
raise ValueError("BaseAdornableNodeMeta can only be used on subclasses of BaseAdornableNode")
|
22
|
+
|
23
|
+
subworkflow_outputs = getattr(subworkflow_attribute, "Outputs")
|
24
|
+
if not issubclass(subworkflow_outputs, BaseOutputs):
|
25
|
+
raise ValueError("subworkflow.Outputs must be a subclass of BaseOutputs")
|
26
|
+
|
27
|
+
outputs_class = dct.get("Outputs")
|
28
|
+
if not outputs_class:
|
29
|
+
raise ValueError("Outputs class not found in base classes")
|
30
|
+
|
31
|
+
if not issubclass(outputs_class, BaseNode.Outputs):
|
32
|
+
raise ValueError("Outputs class must be a subclass of BaseNode.Outputs")
|
33
|
+
|
34
|
+
for descriptor in subworkflow_outputs:
|
35
|
+
node_class.__annotate_outputs_class__(outputs_class, descriptor)
|
36
|
+
|
37
|
+
return node_class
|
38
|
+
|
39
|
+
def __getattribute__(cls, name: str) -> Any:
|
40
|
+
try:
|
41
|
+
return super().__getattribute__(name)
|
42
|
+
except AttributeError:
|
43
|
+
if name != "__wrapped_node__" and issubclass(cls, BaseAdornmentNode):
|
44
|
+
return getattr(cls.__wrapped_node__, name)
|
45
|
+
raise
|
46
|
+
|
47
|
+
@property
|
48
|
+
def _localns(cls) -> Dict[str, Any]:
|
49
|
+
if not hasattr(cls, "SubworkflowInputs"):
|
50
|
+
return super()._localns
|
51
|
+
|
52
|
+
return {
|
53
|
+
**super()._localns,
|
54
|
+
"SubworkflowInputs": getattr(cls, "SubworkflowInputs"),
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
class BaseAdornmentNode(
|
59
|
+
BaseNode[StateType],
|
60
|
+
Generic[StateType],
|
61
|
+
metaclass=_BaseAdornmentNodeMeta,
|
62
|
+
):
|
63
|
+
"""
|
64
|
+
A base node that enables the node to be used as an adornment - meaning it can wrap another node. The
|
65
|
+
wrapped node is stored in the `__wrapped_node__` attribute and is redefined as a single-node subworkflow.
|
66
|
+
"""
|
67
|
+
|
68
|
+
__wrapped_node__: Optional[Type["BaseNode"]] = None
|
69
|
+
subworkflow: Type["BaseWorkflow"]
|
70
|
+
|
71
|
+
@classmethod
|
72
|
+
def __annotate_outputs_class__(cls, outputs_class: Type[BaseOutputs], reference: OutputReference) -> None:
|
73
|
+
# Subclasses of BaseAdornableNode can override this method to provider their own
|
74
|
+
# approach to annotating the outputs class based on the `subworkflow.Outputs`
|
75
|
+
setattr(outputs_class, reference.name, reference)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from uuid import UUID
|
2
2
|
from typing import Optional
|
3
3
|
|
4
|
+
from vellum.client.types.string_vellum_value_request import StringVellumValueRequest
|
4
5
|
from vellum.core.pydantic_utilities import UniversalBaseModel
|
5
6
|
from vellum.workflows.inputs.base import BaseInputs
|
6
7
|
from vellum.workflows.nodes.bases.base import BaseNode
|
@@ -135,3 +136,15 @@ def test_base_node__default_id():
|
|
135
136
|
|
136
137
|
# THEN it should equal the hash of `test_base_node__default_id.<locals>.MyNode`
|
137
138
|
assert my_id == UUID("8e71bea7-ce68-492f-9abe-477c788e6273")
|
139
|
+
|
140
|
+
|
141
|
+
def test_base_node__node_resolution__descriptor_in_fern_pydantic():
|
142
|
+
class State(BaseState):
|
143
|
+
foo: str
|
144
|
+
|
145
|
+
class SomeNode(BaseNode):
|
146
|
+
model = StringVellumValueRequest(value=State.foo)
|
147
|
+
|
148
|
+
node = SomeNode(state=State(foo="bar"))
|
149
|
+
|
150
|
+
assert node.model.value == "bar"
|