vellum-ai 0.14.2__py3-none-any.whl → 0.14.4__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/client/core/client_wrapper.py +1 -1
- vellum/client/resources/document_indexes/client.py +4 -4
- vellum/client/resources/documents/client.py +0 -2
- vellum/client/resources/folder_entities/client.py +4 -8
- vellum/client/resources/test_suite_runs/client.py +0 -2
- vellum/client/types/deployment_read.py +5 -5
- vellum/client/types/deployment_release_tag_read.py +2 -2
- vellum/client/types/document_document_to_document_index.py +5 -5
- vellum/client/types/document_index_read.py +5 -5
- vellum/client/types/document_read.py +1 -1
- vellum/client/types/enriched_normalized_completion.py +3 -3
- vellum/client/types/generate_options_request.py +2 -2
- vellum/client/types/slim_deployment_read.py +5 -5
- vellum/client/types/slim_document.py +3 -3
- vellum/client/types/slim_document_document_to_document_index.py +5 -5
- vellum/client/types/slim_workflow_deployment.py +5 -5
- vellum/client/types/test_suite_run_read.py +5 -5
- vellum/client/types/workflow_deployment_read.py +5 -5
- vellum/client/types/workflow_release_tag_read.py +2 -2
- vellum/prompts/blocks/compilation.py +2 -2
- vellum/prompts/blocks/tests/test_compilation.py +9 -0
- vellum/utils/templating/render.py +1 -1
- vellum/workflows/constants.py +9 -0
- vellum/workflows/nodes/core/map_node/node.py +1 -1
- vellum/workflows/nodes/core/retry_node/node.py +4 -3
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +14 -0
- vellum/workflows/nodes/core/try_node/node.py +1 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +5 -0
- vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +1 -0
- vellum/workflows/tests/test_undefined.py +12 -0
- vellum/workflows/workflows/base.py +59 -0
- vellum/workflows/workflows/tests/test_base_workflow.py +88 -0
- {vellum_ai-0.14.2.dist-info → vellum_ai-0.14.4.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.2.dist-info → vellum_ai-0.14.4.dist-info}/RECORD +45 -44
- vellum_ee/workflows/display/base.py +7 -6
- vellum_ee/workflows/display/nodes/__init__.py +4 -0
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +3 -5
- vellum_ee/workflows/display/types.py +2 -3
- vellum_ee/workflows/display/vellum.py +11 -5
- vellum_ee/workflows/display/workflows/base_workflow_display.py +10 -18
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +7 -41
- vellum_ee/workflows/tests/local_workflow/display/workflow.py +0 -2
- {vellum_ai-0.14.2.dist-info → vellum_ai-0.14.4.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.2.dist-info → vellum_ai-0.14.4.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.2.dist-info → vellum_ai-0.14.4.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.
|
21
|
+
"X-Fern-SDK-Version": "0.14.4",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -54,8 +54,8 @@ class DocumentIndexesClient:
|
|
54
54
|
status : typing.Optional[DocumentIndexesListRequestStatus]
|
55
55
|
Filter down to only document indices that have a status matching the status specified
|
56
56
|
|
57
|
-
|
58
|
-
|
57
|
+
* `ACTIVE` - Active
|
58
|
+
* `ARCHIVED` - Archived
|
59
59
|
|
60
60
|
request_options : typing.Optional[RequestOptions]
|
61
61
|
Request-specific configuration.
|
@@ -589,8 +589,8 @@ class AsyncDocumentIndexesClient:
|
|
589
589
|
status : typing.Optional[DocumentIndexesListRequestStatus]
|
590
590
|
Filter down to only document indices that have a status matching the status specified
|
591
591
|
|
592
|
-
|
593
|
-
|
592
|
+
* `ACTIVE` - Active
|
593
|
+
* `ARCHIVED` - Archived
|
594
594
|
|
595
595
|
request_options : typing.Optional[RequestOptions]
|
596
596
|
Request-specific configuration.
|
@@ -278,7 +278,6 @@ class DocumentsClient:
|
|
278
278
|
**Note:** Uses a base url of `https://documents.vellum.ai`.
|
279
279
|
|
280
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
|
-
|
282
281
|
- `add_to_index_names: list[str]` - Optionally include the names of all indexes that you'd like this document to be included in
|
283
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.
|
284
283
|
- `label: str` - A human-friendly name for this document. Typically the filename.
|
@@ -675,7 +674,6 @@ class AsyncDocumentsClient:
|
|
675
674
|
**Note:** Uses a base url of `https://documents.vellum.ai`.
|
676
675
|
|
677
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:
|
678
|
-
|
679
677
|
- `add_to_index_names: list[str]` - Optionally include the names of all indexes that you'd like this document to be included in
|
680
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.
|
681
679
|
- `label: str` - A human-friendly name for this document. Typically the filename.
|
@@ -39,7 +39,6 @@ class FolderEntitiesClient:
|
|
39
39
|
|
40
40
|
To filter by an entity's parent folder, provide the ID of the parent folder. To filter by the root directory, provide
|
41
41
|
a string representing the entity type of the root directory. Supported root directories include:
|
42
|
-
|
43
42
|
- PROMPT_SANDBOX
|
44
43
|
- WORKFLOW_SANDBOX
|
45
44
|
- DOCUMENT_INDEX
|
@@ -48,8 +47,8 @@ class FolderEntitiesClient:
|
|
48
47
|
entity_status : typing.Optional[FolderEntitiesListRequestEntityStatus]
|
49
48
|
Filter down to only those objects whose entities have a status matching the status specified.
|
50
49
|
|
51
|
-
|
52
|
-
|
50
|
+
* `ACTIVE` - Active
|
51
|
+
* `ARCHIVED` - Archived
|
53
52
|
|
54
53
|
limit : typing.Optional[int]
|
55
54
|
Number of results to return per page.
|
@@ -119,7 +118,6 @@ class FolderEntitiesClient:
|
|
119
118
|
folder_id : str
|
120
119
|
The ID of the folder to which the entity should be added. This can be a UUID of a folder, or the name of a root
|
121
120
|
directory. Supported root directories include:
|
122
|
-
|
123
121
|
- PROMPT_SANDBOX
|
124
122
|
- WORKFLOW_SANDBOX
|
125
123
|
- DOCUMENT_INDEX
|
@@ -190,7 +188,6 @@ class AsyncFolderEntitiesClient:
|
|
190
188
|
|
191
189
|
To filter by an entity's parent folder, provide the ID of the parent folder. To filter by the root directory, provide
|
192
190
|
a string representing the entity type of the root directory. Supported root directories include:
|
193
|
-
|
194
191
|
- PROMPT_SANDBOX
|
195
192
|
- WORKFLOW_SANDBOX
|
196
193
|
- DOCUMENT_INDEX
|
@@ -199,8 +196,8 @@ class AsyncFolderEntitiesClient:
|
|
199
196
|
entity_status : typing.Optional[FolderEntitiesListRequestEntityStatus]
|
200
197
|
Filter down to only those objects whose entities have a status matching the status specified.
|
201
198
|
|
202
|
-
|
203
|
-
|
199
|
+
* `ACTIVE` - Active
|
200
|
+
* `ARCHIVED` - Archived
|
204
201
|
|
205
202
|
limit : typing.Optional[int]
|
206
203
|
Number of results to return per page.
|
@@ -278,7 +275,6 @@ class AsyncFolderEntitiesClient:
|
|
278
275
|
folder_id : str
|
279
276
|
The ID of the folder to which the entity should be added. This can be a UUID of a folder, or the name of a root
|
280
277
|
directory. Supported root directories include:
|
281
|
-
|
282
278
|
- PROMPT_SANDBOX
|
283
279
|
- WORKFLOW_SANDBOX
|
284
280
|
- DOCUMENT_INDEX
|
@@ -163,7 +163,6 @@ class TestSuiteRunsClient:
|
|
163
163
|
|
164
164
|
expand : typing.Optional[typing.Union[str, typing.Sequence[str]]]
|
165
165
|
The response fields to expand for more information.
|
166
|
-
|
167
166
|
- 'results.metric_results.metric_label' expands the metric label for each metric result.
|
168
167
|
- 'results.metric_results.metric_definition' expands the metric definition for each metric result.
|
169
168
|
- 'results.metric_results.metric_definition.name' expands the metric definition name for each metric result.
|
@@ -381,7 +380,6 @@ class AsyncTestSuiteRunsClient:
|
|
381
380
|
|
382
381
|
expand : typing.Optional[typing.Union[str, typing.Sequence[str]]]
|
383
382
|
The response fields to expand for more information.
|
384
|
-
|
385
383
|
- 'results.metric_results.metric_label' expands the metric label for each metric result.
|
386
384
|
- 'results.metric_results.metric_definition' expands the metric definition for each metric result.
|
387
385
|
- 'results.metric_results.metric_definition.name' expands the metric definition name for each metric result.
|
@@ -30,17 +30,17 @@ class DeploymentRead(UniversalBaseModel):
|
|
30
30
|
"""
|
31
31
|
The current status of the deployment
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
* `ACTIVE` - Active
|
34
|
+
* `ARCHIVED` - Archived
|
35
35
|
"""
|
36
36
|
|
37
37
|
environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
|
38
38
|
"""
|
39
39
|
The environment this deployment is used in
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
* `DEVELOPMENT` - Development
|
42
|
+
* `STAGING` - Staging
|
43
|
+
* `PRODUCTION` - Production
|
44
44
|
"""
|
45
45
|
|
46
46
|
last_deployed_on: dt.datetime
|
@@ -18,8 +18,8 @@ class DeploymentReleaseTagRead(UniversalBaseModel):
|
|
18
18
|
"""
|
19
19
|
The source of how the Release Tag was originally created
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
* `SYSTEM` - System
|
22
|
+
* `USER` - User
|
23
23
|
"""
|
24
24
|
|
25
25
|
history_item: DeploymentReleaseTagDeploymentHistoryItem = pydantic.Field()
|
@@ -26,11 +26,11 @@ class DocumentDocumentToDocumentIndex(UniversalBaseModel):
|
|
26
26
|
"""
|
27
27
|
An enum value representing where this document is along its indexing lifecycle for this index.
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
* `AWAITING_PROCESSING` - Awaiting Processing
|
30
|
+
* `QUEUED` - Queued
|
31
|
+
* `INDEXING` - Indexing
|
32
|
+
* `INDEXED` - Indexed
|
33
|
+
* `FAILED` - Failed
|
34
34
|
"""
|
35
35
|
|
36
36
|
extracted_text_file_url: typing.Optional[str] = None
|
@@ -27,17 +27,17 @@ class DocumentIndexRead(UniversalBaseModel):
|
|
27
27
|
"""
|
28
28
|
The current status of the document index
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
* `ACTIVE` - Active
|
31
|
+
* `ARCHIVED` - Archived
|
32
32
|
"""
|
33
33
|
|
34
34
|
environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
|
35
35
|
"""
|
36
36
|
The environment this document index is used in
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
* `DEVELOPMENT` - Development
|
39
|
+
* `STAGING` - Staging
|
40
|
+
* `PRODUCTION` - Production
|
41
41
|
"""
|
42
42
|
|
43
43
|
indexing_config: DocumentIndexIndexingConfig
|
@@ -29,9 +29,9 @@ class EnrichedNormalizedCompletion(UniversalBaseModel):
|
|
29
29
|
"""
|
30
30
|
The reason the generation finished.
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
* `LENGTH` - LENGTH
|
33
|
+
* `STOP` - STOP
|
34
|
+
* `UNKNOWN` - UNKNOWN
|
35
35
|
"""
|
36
36
|
|
37
37
|
logprobs: typing.Optional[NormalizedLogProbs] = pydantic.Field(default=None)
|
@@ -30,17 +30,17 @@ class SlimDeploymentRead(UniversalBaseModel):
|
|
30
30
|
"""
|
31
31
|
The current status of the deployment
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
* `ACTIVE` - Active
|
34
|
+
* `ARCHIVED` - Archived
|
35
35
|
"""
|
36
36
|
|
37
37
|
environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
|
38
38
|
"""
|
39
39
|
The environment this deployment is used in
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
* `DEVELOPMENT` - Development
|
42
|
+
* `STAGING` - Staging
|
43
|
+
* `PRODUCTION` - Production
|
44
44
|
"""
|
45
45
|
|
46
46
|
last_deployed_on: dt.datetime
|
@@ -37,15 +37,15 @@ class SlimDocument(UniversalBaseModel):
|
|
37
37
|
"""
|
38
38
|
An enum value representing why the document could not be processed. Is null unless processing_state is FAILED.
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
* `EXCEEDED_CHARACTER_LIMIT` - Exceeded Character Limit
|
41
|
+
* `INVALID_FILE` - Invalid File
|
42
42
|
"""
|
43
43
|
|
44
44
|
status: typing.Optional[DocumentStatus] = pydantic.Field(default=None)
|
45
45
|
"""
|
46
46
|
The document's current status.
|
47
47
|
|
48
|
-
|
48
|
+
* `ACTIVE` - Active
|
49
49
|
"""
|
50
50
|
|
51
51
|
keywords: typing.Optional[typing.List[str]] = pydantic.Field(default=None)
|
@@ -26,11 +26,11 @@ class SlimDocumentDocumentToDocumentIndex(UniversalBaseModel):
|
|
26
26
|
"""
|
27
27
|
An enum value representing where this document is along its indexing lifecycle for this index.
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
* `AWAITING_PROCESSING` - Awaiting Processing
|
30
|
+
* `QUEUED` - Queued
|
31
|
+
* `INDEXING` - Indexing
|
32
|
+
* `INDEXED` - Indexed
|
33
|
+
* `FAILED` - Failed
|
34
34
|
"""
|
35
35
|
|
36
36
|
if IS_PYDANTIC_V2:
|
@@ -29,17 +29,17 @@ class SlimWorkflowDeployment(UniversalBaseModel):
|
|
29
29
|
"""
|
30
30
|
The current status of the workflow deployment
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
* `ACTIVE` - Active
|
33
|
+
* `ARCHIVED` - Archived
|
34
34
|
"""
|
35
35
|
|
36
36
|
environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
|
37
37
|
"""
|
38
38
|
The environment this workflow deployment is used in
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
* `DEVELOPMENT` - Development
|
41
|
+
* `STAGING` - Staging
|
42
|
+
* `PRODUCTION` - Production
|
43
43
|
"""
|
44
44
|
|
45
45
|
created: dt.datetime
|
@@ -21,11 +21,11 @@ class TestSuiteRunRead(UniversalBaseModel):
|
|
21
21
|
"""
|
22
22
|
The current state of this run
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
* `QUEUED` - Queued
|
25
|
+
* `RUNNING` - Running
|
26
|
+
* `COMPLETE` - Complete
|
27
|
+
* `FAILED` - Failed
|
28
|
+
* `CANCELLED` - Cancelled
|
29
29
|
"""
|
30
30
|
|
31
31
|
exec_config: typing.Optional[TestSuiteRunExecConfig] = pydantic.Field(default=None)
|
@@ -29,17 +29,17 @@ class WorkflowDeploymentRead(UniversalBaseModel):
|
|
29
29
|
"""
|
30
30
|
The current status of the workflow deployment
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
* `ACTIVE` - Active
|
33
|
+
* `ARCHIVED` - Archived
|
34
34
|
"""
|
35
35
|
|
36
36
|
environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
|
37
37
|
"""
|
38
38
|
The environment this workflow deployment is used in
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
* `DEVELOPMENT` - Development
|
41
|
+
* `STAGING` - Staging
|
42
|
+
* `PRODUCTION` - Production
|
43
43
|
"""
|
44
44
|
|
45
45
|
created: dt.datetime
|
@@ -18,8 +18,8 @@ class WorkflowReleaseTagRead(UniversalBaseModel):
|
|
18
18
|
"""
|
19
19
|
The source of how the Release Tag was originally created
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
* `SYSTEM` - System
|
22
|
+
* `USER` - User
|
23
23
|
"""
|
24
24
|
|
25
25
|
history_item: WorkflowReleaseTagWorkflowDeploymentHistoryItem = pydantic.Field()
|
@@ -19,7 +19,7 @@ from vellum.client.types.vellum_audio import VellumAudio
|
|
19
19
|
from vellum.client.types.vellum_image import VellumImage
|
20
20
|
from vellum.prompts.blocks.exceptions import PromptCompilationError
|
21
21
|
from vellum.prompts.blocks.types import CompiledChatMessagePromptBlock, CompiledPromptBlock, CompiledValuePromptBlock
|
22
|
-
from vellum.utils.templating.constants import DEFAULT_JINJA_CUSTOM_FILTERS
|
22
|
+
from vellum.utils.templating.constants import DEFAULT_JINJA_CUSTOM_FILTERS, DEFAULT_JINJA_GLOBALS
|
23
23
|
from vellum.utils.templating.render import render_sandboxed_jinja_template
|
24
24
|
from vellum.utils.typing import cast_not_optional
|
25
25
|
|
@@ -74,7 +74,7 @@ def compile_prompt_blocks(
|
|
74
74
|
template=block.template,
|
75
75
|
input_values={name: inp.value for name, inp in inputs_by_name.items()},
|
76
76
|
jinja_custom_filters=DEFAULT_JINJA_CUSTOM_FILTERS,
|
77
|
-
jinja_globals=
|
77
|
+
jinja_globals=DEFAULT_JINJA_GLOBALS,
|
78
78
|
)
|
79
79
|
jinja_content = StringVellumValue(value=rendered_template)
|
80
80
|
|
@@ -37,6 +37,14 @@ from vellum.prompts.blocks.types import CompiledChatMessagePromptBlock, Compiled
|
|
37
37
|
CompiledValuePromptBlock(content=StringVellumValue(value="Repeat back to me Hello, world!")),
|
38
38
|
],
|
39
39
|
),
|
40
|
+
(
|
41
|
+
[JinjaPromptBlock(template="{{ re.search('test', message).group() }}")],
|
42
|
+
[PromptRequestStringInput(key="message", value="testing")],
|
43
|
+
[VellumVariable(id="1", type="STRING", key="message")],
|
44
|
+
[
|
45
|
+
CompiledValuePromptBlock(content=StringVellumValue(value="test")),
|
46
|
+
],
|
47
|
+
),
|
40
48
|
# Rich Text
|
41
49
|
(
|
42
50
|
[
|
@@ -127,6 +135,7 @@ from vellum.prompts.blocks.types import CompiledChatMessagePromptBlock, Compiled
|
|
127
135
|
"empty",
|
128
136
|
"jinja-no-variables",
|
129
137
|
"jinja-with-variables",
|
138
|
+
"jinja-with-custom-global",
|
130
139
|
"rich-text-no-variables",
|
131
140
|
"rich-text-with-variables",
|
132
141
|
"chat-message",
|
vellum/workflows/constants.py
CHANGED
@@ -3,6 +3,15 @@ from typing import Any, cast
|
|
3
3
|
|
4
4
|
|
5
5
|
class _UndefMeta(type):
|
6
|
+
def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> type:
|
7
|
+
cls.__name__ = "undefined"
|
8
|
+
cls.__qualname__ = "undefined"
|
9
|
+
|
10
|
+
undefined_class = super().__new__(cls, name, bases, attrs)
|
11
|
+
undefined_class.__name__ = "undefined"
|
12
|
+
undefined_class.__qualname__ = "undefined"
|
13
|
+
return undefined_class
|
14
|
+
|
6
15
|
def __repr__(cls) -> str:
|
7
16
|
return "undefined"
|
8
17
|
|
@@ -42,8 +42,8 @@ class MapNode(BaseAdornmentNode[StateType], Generic[StateType, MapNodeItemType])
|
|
42
42
|
Used to map over a list of items and execute a Subworkflow on each iteration.
|
43
43
|
|
44
44
|
items: List[MapNodeItemType] - The items to map over
|
45
|
-
subworkflow: Type["BaseWorkflow[SubworkflowInputs, BaseState]"] - The Subworkflow to execute on each iteration
|
46
45
|
max_concurrency: Optional[int] = None - The maximum number of concurrent subworkflow executions
|
46
|
+
subworkflow: Type["BaseWorkflow"] - The Subworkflow to execute
|
47
47
|
"""
|
48
48
|
|
49
49
|
items: List[MapNodeItemType]
|
@@ -18,9 +18,10 @@ class RetryNode(BaseAdornmentNode[StateType], Generic[StateType]):
|
|
18
18
|
Used to retry a Subworkflow a specified number of times.
|
19
19
|
|
20
20
|
max_attempts: int - The maximum number of attempts to retry the Subworkflow
|
21
|
-
delay: float - The number of seconds to wait between retries
|
22
|
-
retry_on_error_code: Optional[
|
23
|
-
|
21
|
+
delay: float = None - The number of seconds to wait between retries
|
22
|
+
retry_on_error_code: Optional[WorkflowErrorCode] = None - The error code to retry on
|
23
|
+
retry_on_condition: Optional[BaseDescriptor] = None - The condition to retry on
|
24
|
+
subworkflow: Type["BaseWorkflow"] - The Subworkflow to execute
|
24
25
|
"""
|
25
26
|
|
26
27
|
max_attempts: int
|
@@ -220,3 +220,17 @@ def test_templating_node__replace_filter():
|
|
220
220
|
},
|
221
221
|
}
|
222
222
|
]
|
223
|
+
|
224
|
+
|
225
|
+
def test_templating_node__last_chat_message():
|
226
|
+
# GIVEN a templating node that outputs a complex object
|
227
|
+
class LastChatMessageTemplateNode(TemplatingNode[BaseState, List[ChatMessage]]):
|
228
|
+
template = """{{ chat_history[:-1] }}"""
|
229
|
+
inputs = {"chat_history": [ChatMessage(role="USER", text="Hello"), ChatMessage(role="ASSISTANT", text="World")]}
|
230
|
+
|
231
|
+
# WHEN the node is run
|
232
|
+
node = LastChatMessageTemplateNode()
|
233
|
+
outputs = node.run()
|
234
|
+
|
235
|
+
# THEN the output is the expected JSON
|
236
|
+
assert outputs.result == [ChatMessage(role="USER", text="Hello")]
|
@@ -17,7 +17,7 @@ class TryNode(BaseAdornmentNode[StateType], Generic[StateType]):
|
|
17
17
|
"""
|
18
18
|
Used to execute a Subworkflow and handle errors.
|
19
19
|
|
20
|
-
on_error_code: Optional[
|
20
|
+
on_error_code: Optional[WorkflowErrorCode] = None - The error code to handle
|
21
21
|
subworkflow: Type["BaseWorkflow"] - The Subworkflow to execute
|
22
22
|
"""
|
23
23
|
|
@@ -17,6 +17,7 @@ from vellum import (
|
|
17
17
|
)
|
18
18
|
from vellum.client import RequestOptions
|
19
19
|
from vellum.client.types.chat_message_request import ChatMessageRequest
|
20
|
+
from vellum.client.types.prompt_settings import PromptSettings
|
20
21
|
from vellum.workflows.constants import OMIT
|
21
22
|
from vellum.workflows.context import get_parent_context
|
22
23
|
from vellum.workflows.errors import WorkflowErrorCode
|
@@ -53,6 +54,8 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
|
|
53
54
|
parameters: PromptParameters = DEFAULT_PROMPT_PARAMETERS
|
54
55
|
expand_meta: Optional[AdHocExpandMeta] = OMIT
|
55
56
|
|
57
|
+
settings: Optional[PromptSettings] = None
|
58
|
+
|
56
59
|
class Trigger(BasePromptNode.Trigger):
|
57
60
|
merge_behavior = MergeBehavior.AWAIT_ANY
|
58
61
|
|
@@ -60,6 +63,7 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
|
|
60
63
|
input_variables, input_values = self._compile_prompt_inputs()
|
61
64
|
parent_context = get_parent_context()
|
62
65
|
request_options = self.request_options or RequestOptions()
|
66
|
+
|
63
67
|
request_options["additional_body_parameters"] = {
|
64
68
|
"execution_context": {"parent_context": parent_context},
|
65
69
|
**request_options.get("additional_body_parameters", {}),
|
@@ -79,6 +83,7 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
|
|
79
83
|
input_variables=input_variables,
|
80
84
|
parameters=self.parameters,
|
81
85
|
blocks=self.blocks,
|
86
|
+
settings=self.settings,
|
82
87
|
functions=normalized_functions,
|
83
88
|
expand_meta=self.expand_meta,
|
84
89
|
request_options=request_options,
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from vellum.workflows.constants import undefined
|
4
|
+
|
5
|
+
|
6
|
+
def test_undefined__ensure_sensible_error_messages():
|
7
|
+
# WHEN we invoke an invalid operation on `undefined`
|
8
|
+
with pytest.raises(Exception) as e:
|
9
|
+
len(undefined)
|
10
|
+
|
11
|
+
# THEN we get a sensible error message
|
12
|
+
assert str(e.value) == "object of type 'undefined' has no len()"
|
@@ -100,6 +100,30 @@ class _BaseWorkflowMeta(type):
|
|
100
100
|
new_dct,
|
101
101
|
)
|
102
102
|
|
103
|
+
def collect_nodes(graph_item: Union[GraphAttribute, Set[GraphAttribute]]) -> Set[Type[BaseNode]]:
|
104
|
+
nodes: Set[Type[BaseNode]] = set()
|
105
|
+
if isinstance(graph_item, Graph):
|
106
|
+
nodes.update(node for node in graph_item.nodes)
|
107
|
+
elif isinstance(graph_item, set):
|
108
|
+
for item in graph_item:
|
109
|
+
if isinstance(item, Graph):
|
110
|
+
nodes.update(node for node in item.nodes)
|
111
|
+
elif inspect.isclass(item) and issubclass(item, BaseNode):
|
112
|
+
nodes.add(item)
|
113
|
+
elif issubclass(graph_item, BaseNode):
|
114
|
+
nodes.add(graph_item)
|
115
|
+
else:
|
116
|
+
raise ValueError(f"Unexpected graph type: {graph_item.__class__}")
|
117
|
+
return nodes
|
118
|
+
|
119
|
+
graph_nodes = collect_nodes(dct.get("graph", set()))
|
120
|
+
unused_nodes = collect_nodes(dct.get("unused_graphs", set()))
|
121
|
+
|
122
|
+
overlap = graph_nodes & unused_nodes
|
123
|
+
if overlap:
|
124
|
+
node_names = [node.__name__ for node in overlap]
|
125
|
+
raise ValueError(f"Node(s) {', '.join(node_names)} cannot appear in both graph and unused_graphs")
|
126
|
+
|
103
127
|
cls = super().__new__(mcs, name, bases, dct)
|
104
128
|
workflow_class = cast(Type["BaseWorkflow"], cls)
|
105
129
|
workflow_class.__id__ = uuid4_from_hash(workflow_class.__qualname__)
|
@@ -112,6 +136,7 @@ GraphAttribute = Union[Type[BaseNode], Graph, Set[Type[BaseNode]], Set[Graph]]
|
|
112
136
|
class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
113
137
|
__id__: UUID = uuid4_from_hash(__qualname__)
|
114
138
|
graph: ClassVar[GraphAttribute]
|
139
|
+
unused_graphs: ClassVar[Set[GraphAttribute]] # nodes or graphs that are defined but not used in the graph
|
115
140
|
emitters: List[BaseWorkflowEmitter]
|
116
141
|
resolvers: List[BaseWorkflowResolver]
|
117
142
|
|
@@ -196,6 +221,40 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
196
221
|
nodes.add(node)
|
197
222
|
yield node
|
198
223
|
|
224
|
+
@classmethod
|
225
|
+
def get_unused_nodes(cls) -> Iterator[Type[BaseNode]]:
|
226
|
+
"""
|
227
|
+
Returns an iterator over the nodes that are defined but not used in the graph.
|
228
|
+
"""
|
229
|
+
if not hasattr(cls, "unused_graphs"):
|
230
|
+
yield from ()
|
231
|
+
else:
|
232
|
+
nodes = set()
|
233
|
+
for item in cls.unused_graphs:
|
234
|
+
if isinstance(item, Graph):
|
235
|
+
# Item is a graph
|
236
|
+
for node in item.nodes:
|
237
|
+
if node not in nodes:
|
238
|
+
nodes.add(node)
|
239
|
+
yield node
|
240
|
+
elif isinstance(item, set):
|
241
|
+
# Item is a set of graphs or nodes
|
242
|
+
for subitem in item:
|
243
|
+
if isinstance(subitem, Graph):
|
244
|
+
for node in subitem.nodes:
|
245
|
+
if node not in nodes:
|
246
|
+
nodes.add(node)
|
247
|
+
yield node
|
248
|
+
elif issubclass(subitem, BaseNode):
|
249
|
+
if subitem not in nodes:
|
250
|
+
nodes.add(subitem)
|
251
|
+
yield subitem
|
252
|
+
elif issubclass(item, BaseNode):
|
253
|
+
# Item is a node
|
254
|
+
if item not in nodes:
|
255
|
+
nodes.add(item)
|
256
|
+
yield item
|
257
|
+
|
199
258
|
@classmethod
|
200
259
|
def get_entrypoints(cls) -> Iterable[Type[BaseNode]]:
|
201
260
|
return iter({e for g in cls.get_subgraphs() for e in g.entrypoints})
|