vellum-ai 0.12.6__py3-none-any.whl → 0.12.8__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/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
|