vellum-ai 0.12.6__py3-none-any.whl → 0.12.8__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- vellum/client/core/client_wrapper.py +1 -1
- vellum/evaluations/resources.py +1 -1
- vellum/prompts/__init__.py +0 -0
- vellum/prompts/blocks/__init__.py +0 -0
- vellum/prompts/blocks/compilation.py +190 -0
- vellum/prompts/blocks/exceptions.py +2 -0
- vellum/prompts/blocks/tests/__init__.py +0 -0
- vellum/prompts/blocks/tests/test_compilation.py +110 -0
- vellum/prompts/blocks/types.py +36 -0
- vellum/utils/__init__.py +0 -0
- vellum/utils/templating/__init__.py +0 -0
- vellum/utils/templating/constants.py +28 -0
- vellum/{workflows/nodes/core/templating_node → utils/templating}/render.py +1 -1
- vellum/workflows/nodes/core/templating_node/node.py +5 -31
- vellum/workflows/nodes/experimental/README.md +6 -0
- vellum/workflows/nodes/experimental/__init__.py +0 -0
- vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py +5 -0
- vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py +260 -0
- vellum/workflows/sandbox.py +3 -0
- {vellum_ai-0.12.6.dist-info → vellum_ai-0.12.8.dist-info}/METADATA +2 -1
- {vellum_ai-0.12.6.dist-info → vellum_ai-0.12.8.dist-info}/RECORD +32 -18
- vellum_cli/__init__.py +31 -0
- vellum_cli/pull.py +14 -0
- vellum_cli/tests/test_pull.py +29 -0
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -2
- /vellum/{workflows/nodes/core/templating_node → utils/templating}/custom_filters.py +0 -0
- /vellum/{workflows/nodes/core/templating_node → utils/templating}/exceptions.py +0 -0
- /vellum/{evaluations/utils → utils}/typing.py +0 -0
- {vellum_ai-0.12.6.dist-info → vellum_ai-0.12.8.dist-info}/LICENSE +0 -0
- {vellum_ai-0.12.6.dist-info → vellum_ai-0.12.8.dist-info}/WHEEL +0 -0
- {vellum_ai-0.12.6.dist-info → vellum_ai-0.12.8.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.12.
|
21
|
+
"X-Fern-SDK-Version": "0.12.8",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
vellum/evaluations/resources.py
CHANGED
@@ -12,7 +12,6 @@ from vellum.evaluations.constants import DEFAULT_MAX_POLLING_DURATION_MS, DEFAUL
|
|
12
12
|
from vellum.evaluations.exceptions import TestSuiteRunResultsException
|
13
13
|
from vellum.evaluations.utils.env import get_api_key
|
14
14
|
from vellum.evaluations.utils.paginator import PaginatedResults, get_all_results
|
15
|
-
from vellum.evaluations.utils.typing import cast_not_optional
|
16
15
|
from vellum.evaluations.utils.uuid import is_valid_uuid
|
17
16
|
from vellum.types import (
|
18
17
|
ExternalTestCaseExecutionRequest,
|
@@ -24,6 +23,7 @@ from vellum.types import (
|
|
24
23
|
TestSuiteRunMetricOutput,
|
25
24
|
TestSuiteRunState,
|
26
25
|
)
|
26
|
+
from vellum.utils.typing import cast_not_optional
|
27
27
|
|
28
28
|
logger = logging.getLogger(__name__)
|
29
29
|
|
File without changes
|
File without changes
|
@@ -0,0 +1,190 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Optional, cast
|
3
|
+
|
4
|
+
from vellum import (
|
5
|
+
ChatMessage,
|
6
|
+
JsonVellumValue,
|
7
|
+
PromptBlock,
|
8
|
+
PromptRequestInput,
|
9
|
+
RichTextPromptBlock,
|
10
|
+
StringVellumValue,
|
11
|
+
VellumVariable,
|
12
|
+
)
|
13
|
+
from vellum.prompts.blocks.exceptions import PromptCompilationError
|
14
|
+
from vellum.prompts.blocks.types import CompiledChatMessagePromptBlock, CompiledPromptBlock, CompiledValuePromptBlock
|
15
|
+
from vellum.utils.templating.constants import DEFAULT_JINJA_CUSTOM_FILTERS
|
16
|
+
from vellum.utils.templating.render import render_sandboxed_jinja_template
|
17
|
+
from vellum.utils.typing import cast_not_optional
|
18
|
+
|
19
|
+
|
20
|
+
def compile_prompt_blocks(
|
21
|
+
blocks: list[PromptBlock],
|
22
|
+
inputs: list[PromptRequestInput],
|
23
|
+
input_variables: list[VellumVariable],
|
24
|
+
) -> list[CompiledPromptBlock]:
|
25
|
+
"""Compiles a list of Prompt Blocks, performing all variable substitutions and Jinja templating needed."""
|
26
|
+
|
27
|
+
sanitized_inputs = _sanitize_inputs(inputs)
|
28
|
+
|
29
|
+
compiled_blocks: list[CompiledPromptBlock] = []
|
30
|
+
for block in blocks:
|
31
|
+
if block.state == "DISABLED":
|
32
|
+
continue
|
33
|
+
|
34
|
+
if block.block_type == "CHAT_MESSAGE":
|
35
|
+
chat_role = cast_not_optional(block.chat_role)
|
36
|
+
inner_blocks = cast_not_optional(block.blocks)
|
37
|
+
unterminated = block.chat_message_unterminated or False
|
38
|
+
|
39
|
+
inner_prompt_blocks = compile_prompt_blocks(
|
40
|
+
inner_blocks,
|
41
|
+
sanitized_inputs,
|
42
|
+
input_variables,
|
43
|
+
)
|
44
|
+
if not inner_prompt_blocks:
|
45
|
+
continue
|
46
|
+
|
47
|
+
compiled_blocks.append(
|
48
|
+
CompiledChatMessagePromptBlock(
|
49
|
+
role=chat_role,
|
50
|
+
unterminated=unterminated,
|
51
|
+
source=block.chat_source,
|
52
|
+
blocks=[inner for inner in inner_prompt_blocks if inner.block_type == "VALUE"],
|
53
|
+
cache_config=block.cache_config,
|
54
|
+
)
|
55
|
+
)
|
56
|
+
|
57
|
+
elif block.block_type == "JINJA":
|
58
|
+
if block.template is None:
|
59
|
+
continue
|
60
|
+
|
61
|
+
rendered_template = render_sandboxed_jinja_template(
|
62
|
+
template=block.template,
|
63
|
+
input_values={input_.key: input_.value for input_ in sanitized_inputs},
|
64
|
+
jinja_custom_filters=DEFAULT_JINJA_CUSTOM_FILTERS,
|
65
|
+
jinja_globals=DEFAULT_JINJA_CUSTOM_FILTERS,
|
66
|
+
)
|
67
|
+
jinja_content = StringVellumValue(value=rendered_template)
|
68
|
+
|
69
|
+
compiled_blocks.append(
|
70
|
+
CompiledValuePromptBlock(
|
71
|
+
content=jinja_content,
|
72
|
+
cache_config=block.cache_config,
|
73
|
+
)
|
74
|
+
)
|
75
|
+
|
76
|
+
elif block.block_type == "VARIABLE":
|
77
|
+
compiled_input: Optional[PromptRequestInput] = next(
|
78
|
+
(input_ for input_ in sanitized_inputs if input_.key == str(block.input_variable)), None
|
79
|
+
)
|
80
|
+
if compiled_input is None:
|
81
|
+
raise PromptCompilationError(f"Input variable '{block.input_variable}' not found")
|
82
|
+
|
83
|
+
if compiled_input.type == "CHAT_HISTORY":
|
84
|
+
history = cast(list[ChatMessage], compiled_input.value)
|
85
|
+
chat_message_blocks = _compile_chat_messages_as_prompt_blocks(history)
|
86
|
+
compiled_blocks.extend(chat_message_blocks)
|
87
|
+
continue
|
88
|
+
|
89
|
+
if compiled_input.type == "STRING":
|
90
|
+
compiled_blocks.append(
|
91
|
+
CompiledValuePromptBlock(
|
92
|
+
content=StringVellumValue(value=compiled_input.value),
|
93
|
+
cache_config=block.cache_config,
|
94
|
+
)
|
95
|
+
)
|
96
|
+
elif compiled_input == "JSON":
|
97
|
+
compiled_blocks.append(
|
98
|
+
CompiledValuePromptBlock(
|
99
|
+
content=JsonVellumValue(value=compiled_input.value),
|
100
|
+
cache_config=block.cache_config,
|
101
|
+
)
|
102
|
+
)
|
103
|
+
elif compiled_input.type == "CHAT_HISTORY":
|
104
|
+
chat_message_blocks = _compile_chat_messages_as_prompt_blocks(compiled_input.value)
|
105
|
+
compiled_blocks.extend(chat_message_blocks)
|
106
|
+
else:
|
107
|
+
raise ValueError(f"Invalid input type for variable block: {compiled_input.type}")
|
108
|
+
|
109
|
+
elif block.block_type == "RICH_TEXT":
|
110
|
+
value_block = _compile_rich_text_block_as_value_block(block=block, inputs=sanitized_inputs)
|
111
|
+
compiled_blocks.append(value_block)
|
112
|
+
|
113
|
+
elif block.block_type == "FUNCTION_DEFINITION":
|
114
|
+
raise ValueError("Function definitions shouldn't go through compilation process")
|
115
|
+
else:
|
116
|
+
raise ValueError(f"Unknown block_type: {block.block_type}")
|
117
|
+
|
118
|
+
return compiled_blocks
|
119
|
+
|
120
|
+
|
121
|
+
def _compile_chat_messages_as_prompt_blocks(chat_messages: list[ChatMessage]) -> list[CompiledChatMessagePromptBlock]:
|
122
|
+
blocks: list[CompiledChatMessagePromptBlock] = []
|
123
|
+
for chat_message in chat_messages:
|
124
|
+
if chat_message.content is None:
|
125
|
+
continue
|
126
|
+
|
127
|
+
chat_message_blocks = (
|
128
|
+
[
|
129
|
+
CompiledValuePromptBlock(
|
130
|
+
content=item,
|
131
|
+
)
|
132
|
+
for item in chat_message.content.value
|
133
|
+
]
|
134
|
+
if chat_message.content.type == "ARRAY"
|
135
|
+
else [
|
136
|
+
CompiledValuePromptBlock(
|
137
|
+
content=chat_message.content,
|
138
|
+
)
|
139
|
+
]
|
140
|
+
)
|
141
|
+
|
142
|
+
blocks.append(
|
143
|
+
CompiledChatMessagePromptBlock(
|
144
|
+
role=chat_message.role,
|
145
|
+
unterminated=False,
|
146
|
+
blocks=chat_message_blocks,
|
147
|
+
source=chat_message.source,
|
148
|
+
)
|
149
|
+
)
|
150
|
+
|
151
|
+
return blocks
|
152
|
+
|
153
|
+
|
154
|
+
def _compile_rich_text_block_as_value_block(
|
155
|
+
block: RichTextPromptBlock,
|
156
|
+
inputs: list[PromptRequestInput],
|
157
|
+
) -> CompiledValuePromptBlock:
|
158
|
+
value: str = ""
|
159
|
+
for child_block in block.blocks:
|
160
|
+
if child_block.block_type == "PLAIN_TEXT":
|
161
|
+
value += child_block.text
|
162
|
+
elif child_block.block_type == "VARIABLE":
|
163
|
+
variable = next((input_ for input_ in inputs if input_.key == str(child_block.input_variable)), None)
|
164
|
+
if variable is None:
|
165
|
+
raise PromptCompilationError(f"Input variable '{child_block.input_variable}' not found")
|
166
|
+
elif variable.type == "STRING":
|
167
|
+
value += str(variable.value)
|
168
|
+
elif variable.type == "JSON":
|
169
|
+
value += json.dumps(variable.value, indent=4)
|
170
|
+
else:
|
171
|
+
raise PromptCompilationError(
|
172
|
+
f"Input variable '{child_block.input_variable}' must be of type STRING or JSON"
|
173
|
+
)
|
174
|
+
else:
|
175
|
+
raise ValueError(f"Invalid child block_type for RICH_TEXT: {child_block.block_type}")
|
176
|
+
|
177
|
+
return CompiledValuePromptBlock(content=StringVellumValue(value=value), cache_config=block.cache_config)
|
178
|
+
|
179
|
+
|
180
|
+
def _sanitize_inputs(inputs: list[PromptRequestInput]) -> list[PromptRequestInput]:
|
181
|
+
sanitized_inputs: list[PromptRequestInput] = []
|
182
|
+
for input_ in inputs:
|
183
|
+
if input_.type == "CHAT_HISTORY" and input_.value is None:
|
184
|
+
sanitized_inputs.append(input_.model_copy(update={"value": cast(list[ChatMessage], [])}))
|
185
|
+
elif input_.type == "STRING" and input_.value is None:
|
186
|
+
sanitized_inputs.append(input_.model_copy(update={"value": ""}))
|
187
|
+
else:
|
188
|
+
sanitized_inputs.append(input_)
|
189
|
+
|
190
|
+
return sanitized_inputs
|
File without changes
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from vellum import (
|
4
|
+
ChatMessagePromptBlock,
|
5
|
+
JinjaPromptBlock,
|
6
|
+
PlainTextPromptBlock,
|
7
|
+
PromptRequestStringInput,
|
8
|
+
RichTextPromptBlock,
|
9
|
+
StringVellumValue,
|
10
|
+
VariablePromptBlock,
|
11
|
+
VellumVariable,
|
12
|
+
)
|
13
|
+
from vellum.prompts.blocks.compilation import compile_prompt_blocks
|
14
|
+
from vellum.prompts.blocks.types import CompiledChatMessagePromptBlock, CompiledValuePromptBlock
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.mark.parametrize(
|
18
|
+
["blocks", "inputs", "input_variables", "expected"],
|
19
|
+
[
|
20
|
+
# Empty
|
21
|
+
([], [], [], []),
|
22
|
+
# Jinja
|
23
|
+
(
|
24
|
+
[JinjaPromptBlock(template="Hello, world!")],
|
25
|
+
[],
|
26
|
+
[],
|
27
|
+
[
|
28
|
+
CompiledValuePromptBlock(content=StringVellumValue(value="Hello, world!")),
|
29
|
+
],
|
30
|
+
),
|
31
|
+
(
|
32
|
+
[JinjaPromptBlock(template="Repeat back to me {{ echo }}")],
|
33
|
+
[PromptRequestStringInput(key="echo", value="Hello, world!")],
|
34
|
+
[VellumVariable(id="1", type="STRING", key="echo")],
|
35
|
+
[
|
36
|
+
CompiledValuePromptBlock(content=StringVellumValue(value="Repeat back to me Hello, world!")),
|
37
|
+
],
|
38
|
+
),
|
39
|
+
# Rich Text
|
40
|
+
(
|
41
|
+
[
|
42
|
+
RichTextPromptBlock(
|
43
|
+
blocks=[
|
44
|
+
PlainTextPromptBlock(text="Hello, world!"),
|
45
|
+
]
|
46
|
+
)
|
47
|
+
],
|
48
|
+
[],
|
49
|
+
[],
|
50
|
+
[
|
51
|
+
CompiledValuePromptBlock(content=StringVellumValue(value="Hello, world!")),
|
52
|
+
],
|
53
|
+
),
|
54
|
+
(
|
55
|
+
[
|
56
|
+
RichTextPromptBlock(
|
57
|
+
blocks=[
|
58
|
+
PlainTextPromptBlock(text='Repeat back to me "'),
|
59
|
+
VariablePromptBlock(input_variable="echo"),
|
60
|
+
PlainTextPromptBlock(text='".'),
|
61
|
+
]
|
62
|
+
)
|
63
|
+
],
|
64
|
+
[PromptRequestStringInput(key="echo", value="Hello, world!")],
|
65
|
+
[VellumVariable(id="901ec2d6-430c-4341-b963-ca689006f5cc", type="STRING", key="echo")],
|
66
|
+
[
|
67
|
+
CompiledValuePromptBlock(content=StringVellumValue(value='Repeat back to me "Hello, world!".')),
|
68
|
+
],
|
69
|
+
),
|
70
|
+
# Chat Message
|
71
|
+
(
|
72
|
+
[
|
73
|
+
ChatMessagePromptBlock(
|
74
|
+
chat_role="USER",
|
75
|
+
blocks=[
|
76
|
+
RichTextPromptBlock(
|
77
|
+
blocks=[
|
78
|
+
PlainTextPromptBlock(text='Repeat back to me "'),
|
79
|
+
VariablePromptBlock(input_variable="echo"),
|
80
|
+
PlainTextPromptBlock(text='".'),
|
81
|
+
]
|
82
|
+
)
|
83
|
+
],
|
84
|
+
)
|
85
|
+
],
|
86
|
+
[PromptRequestStringInput(key="echo", value="Hello, world!")],
|
87
|
+
[VellumVariable(id="901ec2d6-430c-4341-b963-ca689006f5cc", type="STRING", key="echo")],
|
88
|
+
[
|
89
|
+
CompiledChatMessagePromptBlock(
|
90
|
+
role="USER",
|
91
|
+
blocks=[
|
92
|
+
CompiledValuePromptBlock(content=StringVellumValue(value='Repeat back to me "Hello, world!".'))
|
93
|
+
],
|
94
|
+
),
|
95
|
+
],
|
96
|
+
),
|
97
|
+
],
|
98
|
+
ids=[
|
99
|
+
"empty",
|
100
|
+
"jinja-no-variables",
|
101
|
+
"jinja-with-variables",
|
102
|
+
"rich-text-no-variables",
|
103
|
+
"rich-text-with-variables",
|
104
|
+
"chat-message",
|
105
|
+
],
|
106
|
+
)
|
107
|
+
def test_compile_prompt_blocks__happy(blocks, inputs, input_variables, expected):
|
108
|
+
actual = compile_prompt_blocks(blocks=blocks, inputs=inputs, input_variables=input_variables)
|
109
|
+
|
110
|
+
assert actual == expected
|
@@ -0,0 +1,36 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Annotated, Literal, Optional, Union
|
4
|
+
|
5
|
+
from vellum import ArrayVellumValue, ChatMessageRole, EphemeralPromptCacheConfig, VellumValue
|
6
|
+
from vellum.client.core import UniversalBaseModel
|
7
|
+
|
8
|
+
|
9
|
+
class BaseCompiledPromptBlock(UniversalBaseModel):
|
10
|
+
cache_config: Optional[EphemeralPromptCacheConfig] = None
|
11
|
+
|
12
|
+
|
13
|
+
class CompiledValuePromptBlock(BaseCompiledPromptBlock):
|
14
|
+
block_type: Literal["VALUE"] = "VALUE"
|
15
|
+
content: VellumValue
|
16
|
+
|
17
|
+
|
18
|
+
class CompiledChatMessagePromptBlock(BaseCompiledPromptBlock):
|
19
|
+
block_type: Literal["CHAT_MESSAGE"] = "CHAT_MESSAGE"
|
20
|
+
role: ChatMessageRole = "ASSISTANT"
|
21
|
+
unterminated: bool = False
|
22
|
+
blocks: list[CompiledValuePromptBlock] = []
|
23
|
+
source: Optional[str] = None
|
24
|
+
|
25
|
+
|
26
|
+
CompiledPromptBlock = Annotated[
|
27
|
+
Union[
|
28
|
+
CompiledValuePromptBlock,
|
29
|
+
CompiledChatMessagePromptBlock,
|
30
|
+
],
|
31
|
+
"block_type",
|
32
|
+
]
|
33
|
+
|
34
|
+
ArrayVellumValue.model_rebuild()
|
35
|
+
|
36
|
+
CompiledValuePromptBlock.model_rebuild()
|
vellum/utils/__init__.py
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import datetime
|
2
|
+
import itertools
|
3
|
+
import json
|
4
|
+
import random
|
5
|
+
import re
|
6
|
+
from typing import Any, Callable, Dict, Union
|
7
|
+
|
8
|
+
import dateutil
|
9
|
+
import pydash
|
10
|
+
import pytz
|
11
|
+
import yaml
|
12
|
+
|
13
|
+
from vellum.utils.templating.custom_filters import is_valid_json_string
|
14
|
+
|
15
|
+
DEFAULT_JINJA_GLOBALS: Dict[str, Any] = {
|
16
|
+
"datetime": datetime,
|
17
|
+
"dateutil": dateutil,
|
18
|
+
"itertools": itertools,
|
19
|
+
"json": json,
|
20
|
+
"pydash": pydash,
|
21
|
+
"pytz": pytz,
|
22
|
+
"random": random,
|
23
|
+
"re": re,
|
24
|
+
"yaml": yaml,
|
25
|
+
}
|
26
|
+
DEFAULT_JINJA_CUSTOM_FILTERS: Dict[str, Callable[[Union[str, bytes]], bool]] = {
|
27
|
+
"is_valid_json_string": is_valid_json_string,
|
28
|
+
}
|
@@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, Optional, Union
|
|
3
3
|
|
4
4
|
from jinja2.sandbox import SandboxedEnvironment
|
5
5
|
|
6
|
-
from vellum.
|
6
|
+
from vellum.utils.templating.exceptions import JinjaTemplateError
|
7
7
|
from vellum.workflows.state.encoder import DefaultStateEncoder
|
8
8
|
|
9
9
|
|
@@ -1,42 +1,16 @@
|
|
1
|
-
import datetime
|
2
|
-
import itertools
|
3
|
-
import json
|
4
|
-
import random
|
5
|
-
import re
|
6
1
|
from typing import Any, Callable, ClassVar, Dict, Generic, Mapping, Tuple, Type, TypeVar, Union, get_args
|
7
2
|
|
8
|
-
import
|
9
|
-
import
|
10
|
-
import
|
11
|
-
import yaml
|
12
|
-
|
3
|
+
from vellum.utils.templating.constants import DEFAULT_JINJA_CUSTOM_FILTERS, DEFAULT_JINJA_GLOBALS
|
4
|
+
from vellum.utils.templating.exceptions import JinjaTemplateError
|
5
|
+
from vellum.utils.templating.render import render_sandboxed_jinja_template
|
13
6
|
from vellum.workflows.errors import WorkflowErrorCode
|
14
7
|
from vellum.workflows.exceptions import NodeException
|
15
8
|
from vellum.workflows.nodes.bases import BaseNode
|
16
9
|
from vellum.workflows.nodes.bases.base import BaseNodeMeta
|
17
|
-
from vellum.workflows.nodes.core.templating_node.custom_filters import is_valid_json_string
|
18
|
-
from vellum.workflows.nodes.core.templating_node.exceptions import JinjaTemplateError
|
19
|
-
from vellum.workflows.nodes.core.templating_node.render import render_sandboxed_jinja_template
|
20
10
|
from vellum.workflows.types.core import EntityInputsInterface
|
21
11
|
from vellum.workflows.types.generics import StateType
|
22
12
|
from vellum.workflows.types.utils import get_original_base
|
23
13
|
|
24
|
-
_DEFAULT_JINJA_GLOBALS: Dict[str, Any] = {
|
25
|
-
"datetime": datetime,
|
26
|
-
"dateutil": dateutil,
|
27
|
-
"itertools": itertools,
|
28
|
-
"json": json,
|
29
|
-
"pydash": pydash,
|
30
|
-
"pytz": pytz,
|
31
|
-
"random": random,
|
32
|
-
"re": re,
|
33
|
-
"yaml": yaml,
|
34
|
-
}
|
35
|
-
|
36
|
-
_DEFAULT_JINJA_CUSTOM_FILTERS: Dict[str, Callable[[Union[str, bytes]], bool]] = {
|
37
|
-
"is_valid_json_string": is_valid_json_string,
|
38
|
-
}
|
39
|
-
|
40
14
|
_OutputType = TypeVar("_OutputType")
|
41
15
|
|
42
16
|
|
@@ -78,8 +52,8 @@ class TemplatingNode(BaseNode[StateType], Generic[StateType, _OutputType], metac
|
|
78
52
|
# The inputs to render the template with.
|
79
53
|
inputs: ClassVar[EntityInputsInterface]
|
80
54
|
|
81
|
-
jinja_globals: Dict[str, Any] =
|
82
|
-
jinja_custom_filters: Mapping[str, Callable[[Union[str, bytes]], bool]] =
|
55
|
+
jinja_globals: Dict[str, Any] = DEFAULT_JINJA_GLOBALS
|
56
|
+
jinja_custom_filters: Mapping[str, Callable[[Union[str, bytes]], bool]] = DEFAULT_JINJA_CUSTOM_FILTERS
|
83
57
|
|
84
58
|
class Outputs(BaseNode.Outputs):
|
85
59
|
"""
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# 🧪 Experimental
|
2
|
+
|
3
|
+
This section is a proofing ground for new ideas and concepts. It's not meant to be used in production and is
|
4
|
+
subject to breaking changes at any time.
|
5
|
+
|
6
|
+
If a concept within is sufficiently interesting and validated, then it may be introduced as a first-class node.
|
File without changes
|
@@ -0,0 +1,260 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
from uuid import uuid4
|
5
|
+
from typing import Any, Iterable, Iterator, List, Literal, Union, cast
|
6
|
+
|
7
|
+
from openai import OpenAI
|
8
|
+
from openai.types.chat import (
|
9
|
+
ChatCompletionAssistantMessageParam,
|
10
|
+
ChatCompletionContentPartImageParam,
|
11
|
+
ChatCompletionContentPartInputAudioParam,
|
12
|
+
ChatCompletionContentPartParam,
|
13
|
+
ChatCompletionContentPartRefusalParam,
|
14
|
+
ChatCompletionContentPartTextParam,
|
15
|
+
ChatCompletionMessageParam,
|
16
|
+
ChatCompletionSystemMessageParam,
|
17
|
+
ChatCompletionUserMessageParam,
|
18
|
+
)
|
19
|
+
from openai.types.chat.chat_completion_chunk import Choice
|
20
|
+
|
21
|
+
from vellum import (
|
22
|
+
AdHocExecutePromptEvent,
|
23
|
+
FulfilledAdHocExecutePromptEvent,
|
24
|
+
InitiatedAdHocExecutePromptEvent,
|
25
|
+
RejectedAdHocExecutePromptEvent,
|
26
|
+
StreamingAdHocExecutePromptEvent,
|
27
|
+
StringVellumValue,
|
28
|
+
VellumAudio,
|
29
|
+
VellumError,
|
30
|
+
)
|
31
|
+
from vellum.prompts.blocks.compilation import compile_prompt_blocks
|
32
|
+
from vellum.prompts.blocks.types import CompiledChatMessagePromptBlock
|
33
|
+
from vellum.workflows.errors import WorkflowErrorCode
|
34
|
+
from vellum.workflows.exceptions import NodeException
|
35
|
+
from vellum.workflows.nodes import InlinePromptNode
|
36
|
+
from vellum.workflows.types.generics import StateType
|
37
|
+
|
38
|
+
logger = logging.getLogger(__name__)
|
39
|
+
|
40
|
+
|
41
|
+
class OpenAIChatCompletionNode(InlinePromptNode[StateType]):
|
42
|
+
"""
|
43
|
+
Used to execute a Prompt using the OpenAI API.
|
44
|
+
"""
|
45
|
+
|
46
|
+
# Override
|
47
|
+
def _get_prompt_event_stream(self) -> Iterator[AdHocExecutePromptEvent]:
|
48
|
+
client = self._get_client()
|
49
|
+
|
50
|
+
execution_id = str(uuid4())
|
51
|
+
|
52
|
+
yield InitiatedAdHocExecutePromptEvent(
|
53
|
+
execution_id=execution_id,
|
54
|
+
)
|
55
|
+
|
56
|
+
try:
|
57
|
+
stream = client.chat.completions.create(
|
58
|
+
messages=self._get_messages(),
|
59
|
+
model=self.ml_model,
|
60
|
+
# TODO: Add support for additional parameters
|
61
|
+
stream=True,
|
62
|
+
)
|
63
|
+
except Exception as exc:
|
64
|
+
yield RejectedAdHocExecutePromptEvent(
|
65
|
+
error=VellumError(
|
66
|
+
code=WorkflowErrorCode.PROVIDER_ERROR,
|
67
|
+
message=exc.args[0],
|
68
|
+
),
|
69
|
+
execution_id=execution_id,
|
70
|
+
)
|
71
|
+
return
|
72
|
+
|
73
|
+
combined_delta_content = ""
|
74
|
+
for chunk in stream:
|
75
|
+
choices: List[Choice] = chunk.choices
|
76
|
+
if len(choices) != 1:
|
77
|
+
yield RejectedAdHocExecutePromptEvent(
|
78
|
+
error=VellumError(
|
79
|
+
code=WorkflowErrorCode.PROVIDER_ERROR,
|
80
|
+
message="Expected one choice per chunk, but found more than one.",
|
81
|
+
),
|
82
|
+
execution_id=execution_id,
|
83
|
+
)
|
84
|
+
return
|
85
|
+
|
86
|
+
choice = choices[0]
|
87
|
+
delta = choice.delta
|
88
|
+
|
89
|
+
if delta.tool_calls:
|
90
|
+
# TODO: Add support for tool calls
|
91
|
+
raise NotImplementedError("This node hasn't been extended to support tool calling yet.")
|
92
|
+
|
93
|
+
if delta.content:
|
94
|
+
combined_delta_content += delta.content
|
95
|
+
|
96
|
+
StreamingAdHocExecutePromptEvent(
|
97
|
+
output=StringVellumValue(value=delta.content),
|
98
|
+
# TODO: Add support for multiple outputs
|
99
|
+
output_index=1,
|
100
|
+
execution_id=execution_id,
|
101
|
+
)
|
102
|
+
|
103
|
+
yield FulfilledAdHocExecutePromptEvent(
|
104
|
+
# TODO: Add support for multiple outputs
|
105
|
+
outputs=[
|
106
|
+
StringVellumValue(value=combined_delta_content),
|
107
|
+
],
|
108
|
+
execution_id=execution_id,
|
109
|
+
)
|
110
|
+
|
111
|
+
def _get_client(self) -> OpenAI:
|
112
|
+
"""Used to retrieve an API client for interacting with the OpenAI API.
|
113
|
+
|
114
|
+
Note: This method can be overridden if you'd like to use your own API client that conforms to the same
|
115
|
+
interfaces as that of OpenAI.
|
116
|
+
"""
|
117
|
+
|
118
|
+
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
119
|
+
|
120
|
+
if not openai_api_key:
|
121
|
+
raise NodeException(
|
122
|
+
code=WorkflowErrorCode.INTERNAL_ERROR,
|
123
|
+
message="Unable to determine an OpenAI API key.",
|
124
|
+
)
|
125
|
+
|
126
|
+
client = OpenAI(api_key=openai_api_key)
|
127
|
+
return client
|
128
|
+
|
129
|
+
def _get_messages(self) -> Iterable[ChatCompletionMessageParam]:
|
130
|
+
input_variables, input_values = self._compile_prompt_inputs()
|
131
|
+
|
132
|
+
compiled_blocks = compile_prompt_blocks(
|
133
|
+
blocks=self.blocks, inputs=input_values, input_variables=input_variables
|
134
|
+
)
|
135
|
+
|
136
|
+
chat_message_blocks: list[CompiledChatMessagePromptBlock] = [
|
137
|
+
block for block in compiled_blocks if block.block_type == "CHAT_MESSAGE"
|
138
|
+
]
|
139
|
+
messages = [self._create_message(block) for block in chat_message_blocks]
|
140
|
+
|
141
|
+
return messages
|
142
|
+
|
143
|
+
@classmethod
|
144
|
+
def _create_message(cls, chat_message_block: CompiledChatMessagePromptBlock) -> ChatCompletionMessageParam:
|
145
|
+
name = chat_message_block.source
|
146
|
+
content = cls._create_message_content(chat_message_block)
|
147
|
+
|
148
|
+
if chat_message_block.role == "SYSTEM":
|
149
|
+
relevant_system_content = [
|
150
|
+
cast(ChatCompletionContentPartTextParam, c) for c in content if c["type"] == "text"
|
151
|
+
]
|
152
|
+
system_message: ChatCompletionSystemMessageParam = {
|
153
|
+
"content": relevant_system_content,
|
154
|
+
"role": "system",
|
155
|
+
}
|
156
|
+
if name:
|
157
|
+
system_message["name"] = name
|
158
|
+
|
159
|
+
return system_message
|
160
|
+
elif chat_message_block.role == "USER":
|
161
|
+
user_message: ChatCompletionUserMessageParam = {
|
162
|
+
"content": content,
|
163
|
+
"role": "user",
|
164
|
+
}
|
165
|
+
if name:
|
166
|
+
user_message["name"] = name
|
167
|
+
|
168
|
+
return user_message
|
169
|
+
elif chat_message_block.role == "ASSISTANT":
|
170
|
+
relevant_assistant_content = [
|
171
|
+
cast(Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartRefusalParam], c)
|
172
|
+
for c in content
|
173
|
+
if c["type"] in ["text", "refusal"]
|
174
|
+
]
|
175
|
+
assistant_message: ChatCompletionAssistantMessageParam = {
|
176
|
+
"content": relevant_assistant_content,
|
177
|
+
"role": "assistant",
|
178
|
+
}
|
179
|
+
if name:
|
180
|
+
assistant_message["name"] = name
|
181
|
+
|
182
|
+
return assistant_message
|
183
|
+
else:
|
184
|
+
logger.error(f"Unexpected role: {chat_message_block.role}")
|
185
|
+
raise NodeException(
|
186
|
+
code=WorkflowErrorCode.INTERNAL_ERROR, message="Unexpected role found when compiling prompt blocks"
|
187
|
+
)
|
188
|
+
|
189
|
+
@classmethod
|
190
|
+
def _create_message_content(
|
191
|
+
cls,
|
192
|
+
chat_message_block: CompiledChatMessagePromptBlock,
|
193
|
+
) -> List[ChatCompletionContentPartParam]:
|
194
|
+
content: List[ChatCompletionContentPartParam] = []
|
195
|
+
for block in chat_message_block.blocks:
|
196
|
+
if block.content.type == "STRING":
|
197
|
+
string_value = cast(str, block.content.value)
|
198
|
+
string_content_item: ChatCompletionContentPartTextParam = {"type": "text", "text": string_value}
|
199
|
+
content.append(string_content_item)
|
200
|
+
elif block.content.type == "JSON":
|
201
|
+
json_value = cast(Any, block.content.value)
|
202
|
+
json_content_item: ChatCompletionContentPartTextParam = {"type": "text", "text": json.dumps(json_value)}
|
203
|
+
content.append(json_content_item)
|
204
|
+
elif block.content.type == "IMAGE":
|
205
|
+
image_value = cast(VellumAudio, block.content.value)
|
206
|
+
image_content_item: ChatCompletionContentPartImageParam = {
|
207
|
+
"type": "image_url",
|
208
|
+
"image_url": {"url": image_value.src},
|
209
|
+
}
|
210
|
+
if image_value.metadata and image_value.metadata.get("detail"):
|
211
|
+
detail = image_value.metadata["detail"]
|
212
|
+
|
213
|
+
if detail not in ["auto", "low", "high"]:
|
214
|
+
raise NodeException(
|
215
|
+
code=WorkflowErrorCode.INTERNAL_ERROR,
|
216
|
+
message="Image detail must be one of 'auto', 'low', or 'high.",
|
217
|
+
)
|
218
|
+
|
219
|
+
image_content_item["image_url"]["detail"] = cast(Literal["auto", "low", "high"], detail)
|
220
|
+
|
221
|
+
content.append(image_content_item)
|
222
|
+
elif block.content.type == "AUDIO":
|
223
|
+
audio_value = cast(VellumAudio, block.content.value)
|
224
|
+
audio_value_src_parts = audio_value.src.split(",")
|
225
|
+
if len(audio_value_src_parts) != 2:
|
226
|
+
raise NodeException(
|
227
|
+
code=WorkflowErrorCode.INTERNAL_ERROR, message="Audio data is not properly encoded."
|
228
|
+
)
|
229
|
+
_, cleaned_audio_value = audio_value_src_parts
|
230
|
+
if not audio_value.metadata:
|
231
|
+
raise NodeException(
|
232
|
+
code=WorkflowErrorCode.INTERNAL_ERROR, message="Audio metadata is required for audio input."
|
233
|
+
)
|
234
|
+
audio_format = audio_value.metadata.get("format")
|
235
|
+
if not audio_format:
|
236
|
+
raise NodeException(
|
237
|
+
code=WorkflowErrorCode.INTERNAL_ERROR, message="Audio format is required for audio input."
|
238
|
+
)
|
239
|
+
if audio_format not in {"wav", "mp3"}:
|
240
|
+
raise NodeException(
|
241
|
+
code=WorkflowErrorCode.INTERNAL_ERROR,
|
242
|
+
message="Audio format must be one of 'wav' or 'mp3'.",
|
243
|
+
)
|
244
|
+
|
245
|
+
audio_content_item: ChatCompletionContentPartInputAudioParam = {
|
246
|
+
"type": "input_audio",
|
247
|
+
"input_audio": {
|
248
|
+
"data": cleaned_audio_value,
|
249
|
+
"format": cast(Literal["wav", "mp3"], audio_format),
|
250
|
+
},
|
251
|
+
}
|
252
|
+
|
253
|
+
content.append(audio_content_item)
|
254
|
+
else:
|
255
|
+
raise NodeException(
|
256
|
+
code=WorkflowErrorCode.INTERNAL_ERROR,
|
257
|
+
message=f"Failed to parse chat message block {block.content.type}",
|
258
|
+
)
|
259
|
+
|
260
|
+
return content
|
vellum/workflows/sandbox.py
CHANGED
@@ -49,3 +49,6 @@ class SandboxRunner(Generic[WorkflowType]):
|
|
49
49
|
self._logger.info(f"Just started Node: {event.node_definition.__name__}")
|
50
50
|
elif event.name == "node.execution.fulfilled":
|
51
51
|
self._logger.info(f"Just finished Node: {event.node_definition.__name__}")
|
52
|
+
elif event.name == "node.execution.rejected":
|
53
|
+
self._logger.debug(f"Error: {event.error}")
|
54
|
+
self._logger.error(f"Failed to run Node: {event.node_definition.__name__}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vellum-ai
|
3
|
-
Version: 0.12.
|
3
|
+
Version: 0.12.8
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -25,6 +25,7 @@ Requires-Dist: cdktf (>=0.20.5,<0.21.0)
|
|
25
25
|
Requires-Dist: click (==8.1.7)
|
26
26
|
Requires-Dist: docker (==7.1.0)
|
27
27
|
Requires-Dist: httpx (>=0.21.2)
|
28
|
+
Requires-Dist: openai (>=1.0.0)
|
28
29
|
Requires-Dist: orderly-set (>=5.2.2,<6.0.0)
|
29
30
|
Requires-Dist: publication (==0.0.3)
|
30
31
|
Requires-Dist: pydantic (>=1.9.2)
|
@@ -1,17 +1,17 @@
|
|
1
1
|
vellum_cli/CONTRIBUTING.md,sha256=FtDC7BGxSeMnwCXAUssFsAIElXtmJE-O5Z7BpolcgvI,2935
|
2
2
|
vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
|
3
|
-
vellum_cli/__init__.py,sha256=
|
3
|
+
vellum_cli/__init__.py,sha256=iims87aL6HbwggCAh8JfLBNf3C7tN3TGDmlg7V5W9Lg,8506
|
4
4
|
vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
|
5
5
|
vellum_cli/config.py,sha256=wJQnv3tCgu1BOugg0AOP94yQ-x1yAg8juX_QoFN9Y7w,5223
|
6
6
|
vellum_cli/image_push.py,sha256=SJwhwWJsLjwGNezNVd_oCVpFMfPsAB3dfLWmriZZUtw,4419
|
7
7
|
vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
|
8
|
-
vellum_cli/pull.py,sha256=
|
8
|
+
vellum_cli/pull.py,sha256=AfSQLHwTeq_T22iRbGyefnmrPiddIKb_fqPMSoTlN0Y,6832
|
9
9
|
vellum_cli/push.py,sha256=gcYhIogeYejZIhNm5Cjp0VBECaXLmVQEvZjrPH0-TSU,5337
|
10
10
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
vellum_cli/tests/conftest.py,sha256=eFGwBxib3Nki830lIFintB0b6r4x8T_KMnmzhlTY5x0,1337
|
12
12
|
vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
|
13
13
|
vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
|
14
|
-
vellum_cli/tests/test_pull.py,sha256=
|
14
|
+
vellum_cli/tests/test_pull.py,sha256=Bnf21VjfiRb_j495idz5N8afucagtiktNCtVMvU8tGs,18977
|
15
15
|
vellum_cli/tests/test_push.py,sha256=V2iGcskh2X3OHj2uV5Vx_BhmtyfmUkyx0lrp8DDOExc,5824
|
16
16
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -28,12 +28,12 @@ vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6m
|
|
28
28
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
29
29
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nmPLj8vkbVCS46XQqmHq8Xj8Mr36wCK_vWf26A9KIkw,1505
|
30
30
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=4SSQGecKWHuoGy5YIGJeOZVHGKwTs_8Y-gf3GvsHb0M,8506
|
31
|
-
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=
|
31
|
+
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=qrf2Ne8n2aaoa2xgK3ExCEGI9BA-2g1zmvjSHyzCWmQ,4230
|
32
32
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=EtdqJfhYw03PuT2iyJ6mSAZK4RsQqDie_2AnJAtMelk,13625
|
33
33
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=ygTjSjYDI4DtkxADWub5rhBnRWItMKWF6fezBrgpOKA,1979
|
34
34
|
vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=t5iJQVoRT5g-v2IiUb4kFYdvUVKch0zn27016pzDZoo,2761
|
35
35
|
vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=3TJvHX_Uuf_gr94VkYc_zmNH8I5p71ChIeoAbJZ3ddY,2158
|
36
|
-
vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=
|
36
|
+
vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=DYhxHgFT06CNCpovO4Z-wFPqCjFKxyl89ygFs8AucRQ,7189
|
37
37
|
vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=x5wiuWbRjxNcPGu8BoBEKHwPeqCpHE-vrGjAdM5TJOs,4721
|
38
38
|
vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=AqUlItgSZij12qRKguKVmDbbaLuDy3Cdom5uOlJPqrc,3640
|
39
39
|
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=jzO63B9KiEAncnBqmz2ZTcxjmEHozMEe7WnfpcpsQYg,3195
|
@@ -76,7 +76,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
76
76
|
vellum/client/__init__.py,sha256=o4m7iRZWEV8rP3GkdaztHAjNmjxjWERlarviFoHzuKI,110927
|
77
77
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
78
78
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
79
|
-
vellum/client/core/client_wrapper.py,sha256=
|
79
|
+
vellum/client/core/client_wrapper.py,sha256=oWk19_A_3mvqQM_W3wJgQnObX6YIE918NEp9olVAuEo,1868
|
80
80
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
81
81
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
82
82
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -649,17 +649,23 @@ vellum/errors/not_found_error.py,sha256=gC71YBdPyHR46l3RNTs0v9taVvAY0gWRFrcKpKzb
|
|
649
649
|
vellum/evaluations/__init__.py,sha256=hNsLoHSykqXDJP-MwFvu2lExImxo9KEyEJjt_fdAzpE,77
|
650
650
|
vellum/evaluations/constants.py,sha256=Vteml4_csZsMgo_q3-71E3JRCAoN6308TXLu5nfLhmU,116
|
651
651
|
vellum/evaluations/exceptions.py,sha256=6Xacoyv43fJvVf6Dt6Io5a-f9vF12Tx51jzsQRNSqhY,56
|
652
|
-
vellum/evaluations/resources.py,sha256=
|
652
|
+
vellum/evaluations/resources.py,sha256=33IDRHOjrRsC20JHs0BtRlaSYCY9Gg-AkA6qkPjBrWo,12676
|
653
653
|
vellum/evaluations/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
654
654
|
vellum/evaluations/utils/env.py,sha256=Xj_nxsoU5ox06EOTjRopR4lrigQI6Le6qbWGltYoEGU,276
|
655
655
|
vellum/evaluations/utils/exceptions.py,sha256=dXMAkzqbHV_AP5FjjbegPlfUE0zQDlpA3qOsoOJUxfg,49
|
656
656
|
vellum/evaluations/utils/paginator.py,sha256=rEED_BJAXAM6tM1yMwHePNzszjq_tTq4NbQvi1jWQ_Q,697
|
657
|
-
vellum/evaluations/utils/typing.py,sha256=wx_daFqD69cYkuJTVnvNrpjhqC3uuhbnyJ9_bIwC9OU,327
|
658
657
|
vellum/evaluations/utils/uuid.py,sha256=Ch6wWRgwICxLxJCTl5iE3EdRlZj2zADR-zUMUtjcMWM,214
|
659
658
|
vellum/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
660
659
|
vellum/plugins/pydantic.py,sha256=EbI0pJMhUS9rLPSkzmAELfnCHrWCJzOrU06T8ommwdw,2334
|
661
660
|
vellum/plugins/utils.py,sha256=U9ZY9KdE3RRvbcG01hXxu9CvfJD6Fo7nJDgcHjQn0FI,606
|
662
661
|
vellum/plugins/vellum_mypy.py,sha256=VC15EzjTsXOb9uF1bky4rcxePP-0epMVmCsLB2z4Dh8,24816
|
662
|
+
vellum/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
663
|
+
vellum/prompts/blocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
664
|
+
vellum/prompts/blocks/compilation.py,sha256=hBN_ajq77tSkVyUIcDlO-Qu77PSeJ1OzVhp70NmQd2k,7458
|
665
|
+
vellum/prompts/blocks/exceptions.py,sha256=vmk5PV6Vyw9nKjZYQDUDW0LH8MfQNIgFvFb_mFWdIRI,50
|
666
|
+
vellum/prompts/blocks/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
667
|
+
vellum/prompts/blocks/tests/test_compilation.py,sha256=0DhMoc4huHR6YnNL-0aBLmWSyUfw2BpRq_gEdKsQmAc,3693
|
668
|
+
vellum/prompts/blocks/types.py,sha256=6aSJQco-5kKeadfKVVXF_SrQPlIJgMYVNc-C7so1sY8,975
|
663
669
|
vellum/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
664
670
|
vellum/resources/__init__.py,sha256=sQWK7g_Z4EM7pa7fy6vy3d_DMdTJ4wVcozBn3Lx4Qpo,141
|
665
671
|
vellum/resources/ad_hoc/__init__.py,sha256=UD01D9nS_M7sRKmMbEg4Tv9SlfFj3cWahVxwUEaSLAY,148
|
@@ -1197,6 +1203,13 @@ vellum/types/workflow_result_event_output_data_search_results.py,sha256=UNfCHLQ0
|
|
1197
1203
|
vellum/types/workflow_result_event_output_data_string.py,sha256=rHEVbN0nyf-xoDoSIUEKlUKh6DDoguer4w0iN18JQ2I,178
|
1198
1204
|
vellum/types/workflow_stream_event.py,sha256=PjHGgN0eJm5w-5FJ6__ASC1FU94Gsav_ko5JWkpVvK8,159
|
1199
1205
|
vellum/types/workspace_secret_read.py,sha256=Z6QNXHxVHRdrLXSI31KxngePRwJTVoJYMXVbtPQwrxs,159
|
1206
|
+
vellum/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1207
|
+
vellum/utils/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1208
|
+
vellum/utils/templating/constants.py,sha256=XTNmDsKa7byjw4GMZmzx2dUeYUTeMLZrPgRHcc80Kvc,613
|
1209
|
+
vellum/utils/templating/custom_filters.py,sha256=Q0DahYRHP4KfaUXDt9XxN-DFLBrAxlv90yaVqxScoUw,264
|
1210
|
+
vellum/utils/templating/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
|
1211
|
+
vellum/utils/templating/render.py,sha256=0vgkwhu2A6o64aT4fUdTSLFCEMbeRjAKAuvv2k2LYGY,1772
|
1212
|
+
vellum/utils/typing.py,sha256=wx_daFqD69cYkuJTVnvNrpjhqC3uuhbnyJ9_bIwC9OU,327
|
1200
1213
|
vellum/version.py,sha256=jq-1PlAYxN9AXuaZqbYk9ak27SgE2lw9Ia5gx1b1gVI,76
|
1201
1214
|
vellum/workflows/README.md,sha256=MLNm-ihc0ao6I8gwwOhXQQBf0jOf-EsA9C519ALYI1o,3610
|
1202
1215
|
vellum/workflows/__init__.py,sha256=CssPsbNvN6rDhoLuqpEv7MMKGa51vE6dvAh6U31Pcio,71
|
@@ -1278,10 +1291,7 @@ vellum/workflows/nodes/core/retry_node/node.py,sha256=IjNcpzFmHyBUjOHEoULLbKf85B
|
|
1278
1291
|
vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1279
1292
|
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=QXTnHwmJHISxXjvZMeuuEo0iVugVMJyaJoggI8yKXfI,3132
|
1280
1293
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1281
|
-
vellum/workflows/nodes/core/templating_node/
|
1282
|
-
vellum/workflows/nodes/core/templating_node/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
|
1283
|
-
vellum/workflows/nodes/core/templating_node/node.py,sha256=Q5U5lAW1nhGFJPc94Gxnq1s5RWrbOo7G_AY4n-ABmGg,4504
|
1284
|
-
vellum/workflows/nodes/core/templating_node/render.py,sha256=OpJp0NAH6qcEL6K9lxR0qjpFb75TYNttJR5iCos8tmg,1792
|
1294
|
+
vellum/workflows/nodes/core/templating_node/node.py,sha256=mWNzsa6TLosrD3higKIsiqEv54OIfNY7YzFDkuQgm6k,3964
|
1285
1295
|
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=0BtXeSix7KGIuKzlPFTMLATpNnFPhut1UV_srGptkt0,1120
|
1286
1296
|
vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
|
1287
1297
|
vellum/workflows/nodes/core/try_node/node.py,sha256=_xkpUNIfHXuwjivMBxdU5DegsNge2ITW5nBifWmBPTY,6670
|
@@ -1331,6 +1341,10 @@ vellum/workflows/nodes/displayable/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
1331
1341
|
vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py,sha256=UI_RMmXn9qwB-StnFPvkDd9FctBQAg43wrfouqvPepk,4701
|
1332
1342
|
vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=4CMwDtXwTaEvFfDpA6j2iLqc7S6IICSkvVZOobEpeps,6954
|
1333
1343
|
vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=KqKJtJ0vuNoPuUPMdILmBTt4a2fBBxxun-nmOI7T8jo,2585
|
1344
|
+
vellum/workflows/nodes/experimental/README.md,sha256=eF6DfIL8t-HbF9-mcofOMymKrraiBHDLKTlnBa51ZiE,284
|
1345
|
+
vellum/workflows/nodes/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1346
|
+
vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
|
1347
|
+
vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=1EGeiaT-Zoo6pttQFKKBcdf3dmhAbjKGaErYD5FFwlc,10185
|
1334
1348
|
vellum/workflows/nodes/utils.py,sha256=EZt7CzJmgQBR_GWFpZr8d-oaoti3tolTd2Cv9wm7dKo,1087
|
1335
1349
|
vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
|
1336
1350
|
vellum/workflows/outputs/base.py,sha256=a7W6rNSDSawwGAXYjNTF2iHb9lnZu7WFSOagZIyy__k,7976
|
@@ -1353,7 +1367,7 @@ vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPyc
|
|
1353
1367
|
vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
|
1354
1368
|
vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
|
1355
1369
|
vellum/workflows/runner/runner.py,sha256=RXnLEmSJFbp0u4vKF7rvD2fscuYfcRYkspIJINnvFAI,27607
|
1356
|
-
vellum/workflows/sandbox.py,sha256=
|
1370
|
+
vellum/workflows/sandbox.py,sha256=1aL5BoUgJX6dbIN3pqui20Wk3VyzXV16BUaZz-clmFs,2269
|
1357
1371
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
1358
1372
|
vellum/workflows/state/base.py,sha256=jpSzF1OQd3-fqi6dMGlNsQl-7JnJxCdzWIigmX8Wz-I,14425
|
1359
1373
|
vellum/workflows/state/context.py,sha256=oXiEdNsWJi1coRB85IreTgUeR6_CrWWBXndtLff9S7M,1272
|
@@ -1384,8 +1398,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
|
|
1384
1398
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1385
1399
|
vellum/workflows/workflows/base.py,sha256=zpspOEdO5Ye_0ZvN-Wkzv9iQSiF1sD201ba8lhbnPbs,17086
|
1386
1400
|
vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
|
1387
|
-
vellum_ai-0.12.
|
1388
|
-
vellum_ai-0.12.
|
1389
|
-
vellum_ai-0.12.
|
1390
|
-
vellum_ai-0.12.
|
1391
|
-
vellum_ai-0.12.
|
1401
|
+
vellum_ai-0.12.8.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1402
|
+
vellum_ai-0.12.8.dist-info/METADATA,sha256=FI7BG5Gx3Dd3exq5uqBWnHu8kuqFrVpE_9tcdUuYwdk,5160
|
1403
|
+
vellum_ai-0.12.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1404
|
+
vellum_ai-0.12.8.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1405
|
+
vellum_ai-0.12.8.dist-info/RECORD,,
|
vellum_cli/__init__.py
CHANGED
@@ -130,11 +130,18 @@ Should only be used for debugging purposes.""",
|
|
130
130
|
is_flag=True,
|
131
131
|
help="""Raises an error immediately if there are any issues with the pulling of the Resource.""",
|
132
132
|
)
|
133
|
+
@click.option(
|
134
|
+
"--include-sandbox",
|
135
|
+
is_flag=True,
|
136
|
+
help="""Generates a runnable sandbox.py file containing test data from the Resource's sandbox. \
|
137
|
+
Helpful for running and debugging workflows locally.""",
|
138
|
+
)
|
133
139
|
def pull(
|
134
140
|
ctx: click.Context,
|
135
141
|
include_json: Optional[bool],
|
136
142
|
exclude_code: Optional[bool],
|
137
143
|
strict: Optional[bool],
|
144
|
+
include_sandbox: Optional[bool],
|
138
145
|
) -> None:
|
139
146
|
"""Pull Resources from Vellum"""
|
140
147
|
|
@@ -143,6 +150,7 @@ def pull(
|
|
143
150
|
include_json=include_json,
|
144
151
|
exclude_code=exclude_code,
|
145
152
|
strict=strict,
|
153
|
+
include_sandbox=include_sandbox,
|
146
154
|
)
|
147
155
|
|
148
156
|
|
@@ -171,6 +179,12 @@ Should only be used for debugging purposes.""",
|
|
171
179
|
is_flag=True,
|
172
180
|
help="""Raises an error immediately if there are any issues with the pulling of the Workflow.""",
|
173
181
|
)
|
182
|
+
@click.option(
|
183
|
+
"--include-sandbox",
|
184
|
+
is_flag=True,
|
185
|
+
help="""Generates a runnable sandbox.py file containing test data from the Resource's sandbox. \
|
186
|
+
Helpful for running and debugging workflows locally.""",
|
187
|
+
)
|
174
188
|
def workflows_pull(
|
175
189
|
module: Optional[str],
|
176
190
|
include_json: Optional[bool],
|
@@ -178,6 +192,7 @@ def workflows_pull(
|
|
178
192
|
workflow_deployment: Optional[str],
|
179
193
|
exclude_code: Optional[bool],
|
180
194
|
strict: Optional[bool],
|
195
|
+
include_sandbox: Optional[bool],
|
181
196
|
) -> None:
|
182
197
|
"""
|
183
198
|
Pull Workflows from Vellum. If a module is provided, only the Workflow for that module will be pulled.
|
@@ -191,6 +206,7 @@ def workflows_pull(
|
|
191
206
|
workflow_deployment=workflow_deployment,
|
192
207
|
exclude_code=exclude_code,
|
193
208
|
strict=strict,
|
209
|
+
include_sandbox=include_sandbox,
|
194
210
|
)
|
195
211
|
|
196
212
|
|
@@ -208,10 +224,23 @@ Should only be used for debugging purposes.""",
|
|
208
224
|
help="""Exclude the code definition of the Resource from the pull response. \
|
209
225
|
Should only be used for debugging purposes.""",
|
210
226
|
)
|
227
|
+
@click.option(
|
228
|
+
"--strict",
|
229
|
+
is_flag=True,
|
230
|
+
help="""Raises an error immediately if there are any issues with the pulling of the Resource.""",
|
231
|
+
)
|
232
|
+
@click.option(
|
233
|
+
"--include-sandbox",
|
234
|
+
is_flag=True,
|
235
|
+
help="""Generates a runnable sandbox.py file containing test data from the Resource's sandbox. \
|
236
|
+
Helpful for running and debugging resources locally.""",
|
237
|
+
)
|
211
238
|
def pull_module(
|
212
239
|
ctx: click.Context,
|
213
240
|
include_json: Optional[bool],
|
214
241
|
exclude_code: Optional[bool],
|
242
|
+
strict: Optional[bool],
|
243
|
+
include_sandbox: Optional[bool],
|
215
244
|
) -> None:
|
216
245
|
"""Pull a specific module from Vellum"""
|
217
246
|
|
@@ -220,6 +249,8 @@ def pull_module(
|
|
220
249
|
module=ctx.parent.invoked_subcommand,
|
221
250
|
include_json=include_json,
|
222
251
|
exclude_code=exclude_code,
|
252
|
+
strict=strict,
|
253
|
+
include_sandbox=include_sandbox,
|
223
254
|
)
|
224
255
|
|
225
256
|
|
vellum_cli/pull.py
CHANGED
@@ -84,6 +84,7 @@ def pull_command(
|
|
84
84
|
include_json: Optional[bool] = None,
|
85
85
|
exclude_code: Optional[bool] = None,
|
86
86
|
strict: Optional[bool] = None,
|
87
|
+
include_sandbox: Optional[bool] = None,
|
87
88
|
) -> None:
|
88
89
|
load_dotenv()
|
89
90
|
logger = load_cli_logger()
|
@@ -114,6 +115,8 @@ def pull_command(
|
|
114
115
|
query_parameters["exclude_code"] = exclude_code
|
115
116
|
if strict:
|
116
117
|
query_parameters["strict"] = strict
|
118
|
+
if include_sandbox:
|
119
|
+
query_parameters["include_sandbox"] = include_sandbox
|
117
120
|
|
118
121
|
response = client.workflows.pull(
|
119
122
|
pk,
|
@@ -166,6 +169,17 @@ def pull_command(
|
|
166
169
|
Its schema should be considered unstable and subject to change at any time."""
|
167
170
|
)
|
168
171
|
|
172
|
+
if include_sandbox:
|
173
|
+
if not workflow_config.ignore:
|
174
|
+
workflow_config.ignore = "sandbox.py"
|
175
|
+
save_lock_file = True
|
176
|
+
elif isinstance(workflow_config.ignore, str) and "sandbox.py" != workflow_config.ignore:
|
177
|
+
workflow_config.ignore = [workflow_config.ignore, "sandbox.py"]
|
178
|
+
save_lock_file = True
|
179
|
+
elif isinstance(workflow_config.ignore, list) and "sandbox.py" not in workflow_config.ignore:
|
180
|
+
workflow_config.ignore.append("sandbox.py")
|
181
|
+
save_lock_file = True
|
182
|
+
|
169
183
|
if save_lock_file:
|
170
184
|
config.save()
|
171
185
|
|
vellum_cli/tests/test_pull.py
CHANGED
@@ -486,3 +486,32 @@ def test_pull__strict__with_error(vellum_client, mock_module):
|
|
486
486
|
vellum_client.workflows.pull.assert_called_once()
|
487
487
|
call_args = vellum_client.workflows.pull.call_args.kwargs
|
488
488
|
assert call_args["request_options"]["additional_query_parameters"] == {"strict": True}
|
489
|
+
|
490
|
+
|
491
|
+
def test_pull__include_sandbox(vellum_client, mock_module):
|
492
|
+
# GIVEN a module on the user's filesystem
|
493
|
+
module = mock_module.module
|
494
|
+
temp_dir = mock_module.temp_dir
|
495
|
+
|
496
|
+
# AND the workflow pull API call returns a zip file
|
497
|
+
vellum_client.workflows.pull.return_value = iter(
|
498
|
+
[_zip_file_map({"workflow.py": "print('hello')", "sandbox.py": "print('hello')"})]
|
499
|
+
)
|
500
|
+
|
501
|
+
# WHEN the user runs the pull command
|
502
|
+
runner = CliRunner()
|
503
|
+
result = runner.invoke(cli_main, ["pull", module, "--include-sandbox"])
|
504
|
+
|
505
|
+
# THEN the command returns successfully
|
506
|
+
assert result.exit_code == 0, result.output
|
507
|
+
|
508
|
+
# AND the pull api is called with include_sandbox=True
|
509
|
+
vellum_client.workflows.pull.assert_called_once()
|
510
|
+
call_args = vellum_client.workflows.pull.call_args.kwargs
|
511
|
+
assert call_args["request_options"]["additional_query_parameters"] == {"include_sandbox": True}
|
512
|
+
|
513
|
+
# AND the sandbox.py should be added to the ignore list
|
514
|
+
lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
515
|
+
with open(lock_json) as f:
|
516
|
+
lock_data = json.load(f)
|
517
|
+
assert lock_data["workflows"][0]["ignore"] == "sandbox.py"
|
@@ -27,8 +27,8 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
|
|
27
27
|
node = self._node
|
28
28
|
node_id = self.node_id
|
29
29
|
raw_code = raise_if_descriptor(node.code)
|
30
|
-
code_value = None
|
31
30
|
|
31
|
+
code_value: Optional[str]
|
32
32
|
if raw_code:
|
33
33
|
code_value = raw_code
|
34
34
|
else:
|
@@ -104,13 +104,12 @@ class BaseInlinePromptNodeDisplay(BaseNodeVellumDisplay[_InlinePromptNodeType],
|
|
104
104
|
chat_properties: JsonObject = {
|
105
105
|
"chat_role": prompt_block.chat_role,
|
106
106
|
"chat_source": prompt_block.chat_source,
|
107
|
+
"chat_message_unterminated": bool(prompt_block.chat_message_unterminated),
|
107
108
|
"blocks": [
|
108
109
|
self._generate_prompt_block(block, input_variable_id_by_name, path + [i])
|
109
110
|
for i, block in enumerate(prompt_block.blocks)
|
110
111
|
],
|
111
112
|
}
|
112
|
-
if prompt_block.chat_message_unterminated is not None:
|
113
|
-
chat_properties["chat_message_unterminated"] = prompt_block.chat_message_unterminated
|
114
113
|
|
115
114
|
block = {
|
116
115
|
"block_type": "CHAT_MESSAGE",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|