vellum-ai 0.14.68__py3-none-any.whl → 0.14.70__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. vellum/__init__.py +4 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +4 -0
  4. vellum/client/types/fast_embed_vectorizer_baai_bge_small_en_v_15.py +23 -0
  5. vellum/client/types/fast_embed_vectorizer_baai_bge_small_en_v_15_request.py +23 -0
  6. vellum/client/types/folder_entity_document_index_data.py +2 -0
  7. vellum/client/types/indexing_config_vectorizer.py +2 -0
  8. vellum/client/types/indexing_config_vectorizer_request.py +2 -0
  9. vellum/types/fast_embed_vectorizer_baai_bge_small_en_v_15.py +3 -0
  10. vellum/types/fast_embed_vectorizer_baai_bge_small_en_v_15_request.py +3 -0
  11. vellum/workflows/environment/__init__.py +2 -1
  12. vellum/workflows/environment/environment.py +5 -1
  13. vellum/workflows/nodes/displayable/bases/search_node.py +15 -3
  14. vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py +215 -0
  15. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +77 -1
  16. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +2 -2
  17. vellum/workflows/references/environment_variable.py +2 -3
  18. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/METADATA +1 -1
  19. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/RECORD +54 -45
  20. vellum_cli/__init__.py +5 -2
  21. vellum_cli/image_push.py +24 -1
  22. vellum_cli/tests/test_image_push.py +103 -12
  23. vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
  24. vellum_ee/workflows/display/nodes/utils.py +2 -2
  25. vellum_ee/workflows/display/nodes/vellum/api_node.py +2 -2
  26. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  27. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
  28. vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -1
  29. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +2 -2
  30. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -1
  31. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +4 -4
  32. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -1
  33. vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -1
  34. vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -1
  35. vellum_ee/workflows/display/nodes/vellum/note_node.py +1 -0
  36. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -1
  37. vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -1
  38. vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
  39. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -1
  40. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  41. vellum_ee/workflows/display/nodes/vellum/tests/test_inline_subworkflow_node.py +88 -0
  42. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +16 -0
  43. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +81 -0
  44. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +9 -1
  45. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +59 -297
  46. vellum_ee/workflows/display/utils/auto_layout.py +130 -0
  47. vellum_ee/workflows/display/utils/expressions.py +7 -0
  48. vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
  49. vellum_ee/workflows/display/utils/tests/test_auto_layout.py +56 -0
  50. vellum_ee/workflows/display/workflows/base_workflow_display.py +15 -10
  51. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +41 -0
  52. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/LICENSE +0 -0
  53. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/WHEEL +0 -0
  54. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/entry_points.txt +0 -0
vellum/__init__.py CHANGED
@@ -123,6 +123,8 @@ from .types import (
123
123
  ExternalInputDescriptor,
124
124
  ExternalTestCaseExecution,
125
125
  ExternalTestCaseExecutionRequest,
126
+ FastEmbedVectorizerBaaiBgeSmallEnV15,
127
+ FastEmbedVectorizerBaaiBgeSmallEnV15Request,
126
128
  FinishReasonEnum,
127
129
  FolderEntity,
128
130
  FolderEntityDocumentIndex,
@@ -750,6 +752,8 @@ __all__ = [
750
752
  "ExternalInputDescriptor",
751
753
  "ExternalTestCaseExecution",
752
754
  "ExternalTestCaseExecutionRequest",
755
+ "FastEmbedVectorizerBaaiBgeSmallEnV15",
756
+ "FastEmbedVectorizerBaaiBgeSmallEnV15Request",
753
757
  "FinishReasonEnum",
754
758
  "FolderEntitiesListRequestEntityStatus",
755
759
  "FolderEntity",
@@ -18,7 +18,7 @@ class BaseClientWrapper:
18
18
  headers: typing.Dict[str, str] = {
19
19
  "X-Fern-Language": "Python",
20
20
  "X-Fern-SDK-Name": "vellum-ai",
21
- "X-Fern-SDK-Version": "0.14.68",
21
+ "X-Fern-SDK-Version": "0.14.70",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -127,6 +127,8 @@ from .execution_vellum_value import ExecutionVellumValue
127
127
  from .external_input_descriptor import ExternalInputDescriptor
128
128
  from .external_test_case_execution import ExternalTestCaseExecution
129
129
  from .external_test_case_execution_request import ExternalTestCaseExecutionRequest
130
+ from .fast_embed_vectorizer_baai_bge_small_en_v_15 import FastEmbedVectorizerBaaiBgeSmallEnV15
131
+ from .fast_embed_vectorizer_baai_bge_small_en_v_15_request import FastEmbedVectorizerBaaiBgeSmallEnV15Request
130
132
  from .finish_reason_enum import FinishReasonEnum
131
133
  from .folder_entity import FolderEntity
132
134
  from .folder_entity_document_index import FolderEntityDocumentIndex
@@ -737,6 +739,8 @@ __all__ = [
737
739
  "ExternalInputDescriptor",
738
740
  "ExternalTestCaseExecution",
739
741
  "ExternalTestCaseExecutionRequest",
742
+ "FastEmbedVectorizerBaaiBgeSmallEnV15",
743
+ "FastEmbedVectorizerBaaiBgeSmallEnV15Request",
740
744
  "FinishReasonEnum",
741
745
  "FolderEntity",
742
746
  "FolderEntityDocumentIndex",
@@ -0,0 +1,23 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ..core.pydantic_utilities import UniversalBaseModel
4
+ import typing
5
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
6
+ import pydantic
7
+
8
+
9
+ class FastEmbedVectorizerBaaiBgeSmallEnV15(UniversalBaseModel):
10
+ """
11
+ FastEmbed vectorizer for BAAI/bge-small-en-v1.5.
12
+ """
13
+
14
+ model_name: typing.Literal["BAAI/bge-small-en-v1.5"] = "BAAI/bge-small-en-v1.5"
15
+
16
+ if IS_PYDANTIC_V2:
17
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
18
+ else:
19
+
20
+ class Config:
21
+ frozen = True
22
+ smart_union = True
23
+ extra = pydantic.Extra.allow
@@ -0,0 +1,23 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ..core.pydantic_utilities import UniversalBaseModel
4
+ import typing
5
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
6
+ import pydantic
7
+
8
+
9
+ class FastEmbedVectorizerBaaiBgeSmallEnV15Request(UniversalBaseModel):
10
+ """
11
+ FastEmbed vectorizer for BAAI/bge-small-en-v1.5.
12
+ """
13
+
14
+ model_name: typing.Literal["BAAI/bge-small-en-v1.5"] = "BAAI/bge-small-en-v1.5"
15
+
16
+ if IS_PYDANTIC_V2:
17
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
18
+ else:
19
+
20
+ class Config:
21
+ frozen = True
22
+ smart_union = True
23
+ extra = pydantic.Extra.allow
@@ -3,6 +3,7 @@
3
3
  from ..core.pydantic_utilities import UniversalBaseModel
4
4
  import datetime as dt
5
5
  from .entity_status import EntityStatus
6
+ from .document_index_indexing_config import DocumentIndexIndexingConfig
6
7
  from ..core.pydantic_utilities import IS_PYDANTIC_V2
7
8
  import typing
8
9
  import pydantic
@@ -14,6 +15,7 @@ class FolderEntityDocumentIndexData(UniversalBaseModel):
14
15
  created: dt.datetime
15
16
  modified: dt.datetime
16
17
  status: EntityStatus
18
+ indexing_config: DocumentIndexIndexingConfig
17
19
 
18
20
  if IS_PYDANTIC_V2:
19
21
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -16,6 +16,7 @@ from .google_vertex_ai_vectorizer_text_embedding_004 import GoogleVertexAiVector
16
16
  from .google_vertex_ai_vectorizer_text_multilingual_embedding_002 import (
17
17
  GoogleVertexAiVectorizerTextMultilingualEmbedding002,
18
18
  )
19
+ from .fast_embed_vectorizer_baai_bge_small_en_v_15 import FastEmbedVectorizerBaaiBgeSmallEnV15
19
20
 
20
21
  IndexingConfigVectorizer = typing.Union[
21
22
  OpenAiVectorizerTextEmbedding3Small,
@@ -27,4 +28,5 @@ IndexingConfigVectorizer = typing.Union[
27
28
  HkunlpInstructorXlVectorizer,
28
29
  GoogleVertexAiVectorizerTextEmbedding004,
29
30
  GoogleVertexAiVectorizerTextMultilingualEmbedding002,
31
+ FastEmbedVectorizerBaaiBgeSmallEnV15,
30
32
  ]
@@ -16,6 +16,7 @@ from .google_vertex_ai_vectorizer_text_embedding_004_request import GoogleVertex
16
16
  from .google_vertex_ai_vectorizer_text_multilingual_embedding_002_request import (
17
17
  GoogleVertexAiVectorizerTextMultilingualEmbedding002Request,
18
18
  )
19
+ from .fast_embed_vectorizer_baai_bge_small_en_v_15_request import FastEmbedVectorizerBaaiBgeSmallEnV15Request
19
20
 
20
21
  IndexingConfigVectorizerRequest = typing.Union[
21
22
  OpenAiVectorizerTextEmbedding3SmallRequest,
@@ -27,4 +28,5 @@ IndexingConfigVectorizerRequest = typing.Union[
27
28
  HkunlpInstructorXlVectorizerRequest,
28
29
  GoogleVertexAiVectorizerTextEmbedding004Request,
29
30
  GoogleVertexAiVectorizerTextMultilingualEmbedding002Request,
31
+ FastEmbedVectorizerBaaiBgeSmallEnV15Request,
30
32
  ]
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.fast_embed_vectorizer_baai_bge_small_en_v_15 import *
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.fast_embed_vectorizer_baai_bge_small_en_v_15_request import *
@@ -1,5 +1,6 @@
1
- from .environment import Environment
1
+ from .environment import Environment, EnvironmentVariables
2
2
 
3
3
  __all__ = [
4
+ "EnvironmentVariables",
4
5
  "Environment",
5
6
  ]
@@ -3,7 +3,11 @@ from typing import Optional
3
3
  from vellum.workflows.references import EnvironmentVariableReference
4
4
 
5
5
 
6
- class Environment:
6
+ class EnvironmentVariables:
7
7
  @staticmethod
8
8
  def get(name: str, default: Optional[str] = None) -> EnvironmentVariableReference:
9
9
  return EnvironmentVariableReference(name=name, default=default)
10
+
11
+
12
+ # Deprecated: Use EnvironmentVariables instead. Will be removed in v0.15.0
13
+ Environment = EnvironmentVariables
@@ -106,11 +106,23 @@ class BaseSearchNode(BaseNode[StateType], Generic[StateType]):
106
106
  message=f"Document Index '{self.document_index}' not found",
107
107
  code=WorkflowErrorCode.INVALID_INPUTS,
108
108
  )
109
- except ApiError:
109
+ except ApiError as e:
110
+ if e.status_code and e.status_code == 403 and isinstance(e.body, dict):
111
+ raise NodeException(
112
+ message=e.body.get("detail", "Provider credentials is missing or unavailable"),
113
+ code=WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE,
114
+ )
115
+ elif e.status_code and e.status_code >= 400 and e.status_code < 500 and isinstance(e.body, dict):
116
+ raise NodeException(
117
+ message=e.body.get(
118
+ "detail", f"An error occurred while searching against Document Index '{self.document_index}'"
119
+ ),
120
+ code=WorkflowErrorCode.INVALID_INPUTS,
121
+ ) from e
110
122
  raise NodeException(
111
- message=f"An error occurred while searching against Document Index '{self.document_index}'", # noqa: E501
123
+ message=f"An error occurred while searching against Document Index '{self.document_index}'",
112
124
  code=WorkflowErrorCode.INTERNAL_ERROR,
113
- )
125
+ ) from e
114
126
 
115
127
  def _get_options_request(self) -> SearchRequestOptionsRequest:
116
128
  return SearchRequestOptionsRequest(
@@ -0,0 +1,215 @@
1
+ import pytest
2
+
3
+ from vellum.client.core.api_error import ApiError
4
+ from vellum.workflows.errors.types import WorkflowErrorCode
5
+ from vellum.workflows.exceptions import NodeException
6
+ from vellum.workflows.inputs import BaseInputs
7
+ from vellum.workflows.nodes.displayable.search_node import SearchNode as BaseSearchNode
8
+ from vellum.workflows.state import BaseState
9
+ from vellum.workflows.state.base import StateMeta
10
+
11
+
12
+ def test_search_node_handles_403_error(vellum_client):
13
+ """Test that SearchNode properly handles 403 API errors with user-facing messages."""
14
+
15
+ class Inputs(BaseInputs):
16
+ query: str
17
+ document_index: str
18
+
19
+ class State(BaseState):
20
+ pass
21
+
22
+ class SearchNode(BaseSearchNode):
23
+ query = Inputs.query
24
+ document_index = Inputs.document_index
25
+
26
+ vellum_client.search.side_effect = ApiError(
27
+ status_code=403, body={"detail": "Provider credentials is missing or unavailable"}
28
+ )
29
+
30
+ node = SearchNode(
31
+ state=State(
32
+ meta=StateMeta(
33
+ workflow_inputs=Inputs(
34
+ query="test query",
35
+ document_index="test-index",
36
+ )
37
+ ),
38
+ )
39
+ )
40
+
41
+ with pytest.raises(NodeException) as exc_info:
42
+ node.run()
43
+
44
+ assert exc_info.value.code == WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE
45
+ assert exc_info.value.message == "Provider credentials is missing or unavailable"
46
+
47
+
48
+ def test_search_node_handles_403_error_with_custom_detail(vellum_client):
49
+ """Test that SearchNode properly handles 403 API errors with custom detail message."""
50
+
51
+ class Inputs(BaseInputs):
52
+ query: str
53
+ document_index: str
54
+
55
+ class State(BaseState):
56
+ pass
57
+
58
+ class SearchNode(BaseSearchNode):
59
+ query = Inputs.query
60
+ document_index = Inputs.document_index
61
+
62
+ vellum_client.search.side_effect = ApiError(status_code=403, body={"detail": "Access denied to document index"})
63
+
64
+ node = SearchNode(
65
+ state=State(
66
+ meta=StateMeta(
67
+ workflow_inputs=Inputs(
68
+ query="test query",
69
+ document_index="test-index",
70
+ )
71
+ ),
72
+ )
73
+ )
74
+
75
+ with pytest.raises(NodeException) as exc_info:
76
+ node.run()
77
+
78
+ assert exc_info.value.code == WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE
79
+ assert exc_info.value.message == "Access denied to document index"
80
+
81
+
82
+ def test_search_node_handles_403_error_without_detail(vellum_client):
83
+ """Test that SearchNode properly handles 403 API errors without detail in body."""
84
+
85
+ class Inputs(BaseInputs):
86
+ query: str
87
+ document_index: str
88
+
89
+ class State(BaseState):
90
+ pass
91
+
92
+ class SearchNode(BaseSearchNode):
93
+ query = Inputs.query
94
+ document_index = Inputs.document_index
95
+
96
+ vellum_client.search.side_effect = ApiError(status_code=403, body={})
97
+
98
+ node = SearchNode(
99
+ state=State(
100
+ meta=StateMeta(
101
+ workflow_inputs=Inputs(
102
+ query="test query",
103
+ document_index="test-index",
104
+ )
105
+ ),
106
+ )
107
+ )
108
+
109
+ with pytest.raises(NodeException) as exc_info:
110
+ node.run()
111
+
112
+ assert exc_info.value.code == WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE
113
+ assert exc_info.value.message == "Provider credentials is missing or unavailable"
114
+
115
+
116
+ def test_search_node_handles_other_4xx_errors(vellum_client):
117
+ """Test that SearchNode properly handles other 4xx API errors."""
118
+
119
+ class Inputs(BaseInputs):
120
+ query: str
121
+ document_index: str
122
+
123
+ class State(BaseState):
124
+ pass
125
+
126
+ class SearchNode(BaseSearchNode):
127
+ query = Inputs.query
128
+ document_index = Inputs.document_index
129
+
130
+ vellum_client.search.side_effect = ApiError(status_code=400, body={"detail": "Invalid request parameters"})
131
+
132
+ node = SearchNode(
133
+ state=State(
134
+ meta=StateMeta(
135
+ workflow_inputs=Inputs(
136
+ query="test query",
137
+ document_index="test-index",
138
+ )
139
+ ),
140
+ )
141
+ )
142
+
143
+ with pytest.raises(NodeException) as exc_info:
144
+ node.run()
145
+
146
+ assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
147
+ assert "Invalid request parameters" in exc_info.value.message
148
+
149
+
150
+ def test_search_node_handles_5xx_errors(vellum_client):
151
+ """Test that SearchNode properly handles 5xx API errors as internal errors."""
152
+
153
+ class Inputs(BaseInputs):
154
+ query: str
155
+ document_index: str
156
+
157
+ class State(BaseState):
158
+ pass
159
+
160
+ class SearchNode(BaseSearchNode):
161
+ query = Inputs.query
162
+ document_index = Inputs.document_index
163
+
164
+ vellum_client.search.side_effect = ApiError(status_code=500, body={"detail": "Internal server error"})
165
+
166
+ node = SearchNode(
167
+ state=State(
168
+ meta=StateMeta(
169
+ workflow_inputs=Inputs(
170
+ query="test query",
171
+ document_index="test-index",
172
+ )
173
+ ),
174
+ )
175
+ )
176
+
177
+ with pytest.raises(NodeException) as exc_info:
178
+ node.run()
179
+
180
+ assert exc_info.value.code == WorkflowErrorCode.INTERNAL_ERROR
181
+ assert "An error occurred while searching against Document Index 'test-index'" in exc_info.value.message
182
+
183
+
184
+ def test_search_node_handles_api_error_without_status_code(vellum_client):
185
+ """Test that SearchNode properly handles API errors without status code."""
186
+
187
+ class Inputs(BaseInputs):
188
+ query: str
189
+ document_index: str
190
+
191
+ class State(BaseState):
192
+ pass
193
+
194
+ class SearchNode(BaseSearchNode):
195
+ query = Inputs.query
196
+ document_index = Inputs.document_index
197
+
198
+ vellum_client.search.side_effect = ApiError(status_code=None, body={"detail": "Unknown error"})
199
+
200
+ node = SearchNode(
201
+ state=State(
202
+ meta=StateMeta(
203
+ workflow_inputs=Inputs(
204
+ query="test query",
205
+ document_index="test-index",
206
+ )
207
+ ),
208
+ )
209
+ )
210
+
211
+ with pytest.raises(NodeException) as exc_info:
212
+ node.run()
213
+
214
+ assert exc_info.value.code == WorkflowErrorCode.INTERNAL_ERROR
215
+ assert "An error occurred while searching against Document Index 'test-index'" in exc_info.value.message
@@ -1,7 +1,17 @@
1
+ import json
2
+ from typing import Any, List
3
+
4
+ from vellum import ChatMessage
1
5
  from vellum.client.types.function_call import FunctionCall
2
6
  from vellum.client.types.function_call_vellum_value import FunctionCallVellumValue
3
- from vellum.workflows.nodes.experimental.tool_calling_node.utils import create_tool_router_node
7
+ from vellum.client.types.string_chat_message_content import StringChatMessageContent
8
+ from vellum.workflows import BaseWorkflow
9
+ from vellum.workflows.inputs.base import BaseInputs
10
+ from vellum.workflows.nodes.bases import BaseNode
11
+ from vellum.workflows.nodes.experimental.tool_calling_node.utils import create_function_node, create_tool_router_node
12
+ from vellum.workflows.outputs.base import BaseOutputs
4
13
  from vellum.workflows.state.base import BaseState, StateMeta
14
+ from vellum.workflows.state.context import WorkflowContext
5
15
 
6
16
 
7
17
  def first_function() -> str:
@@ -51,3 +61,69 @@ def test_port_condition_match_function_name():
51
61
  # AND the default port should be false
52
62
  default_port = getattr(router_node.Ports, "default")
53
63
  assert default_port.resolve_condition(state) is False
64
+
65
+
66
+ def test_tool_calling_node_inline_workflow_context():
67
+ """
68
+ Test that the tool calling node correctly passes the context to the inline workflow.
69
+ This specifically tests that inline workflows receive the correct context.
70
+ """
71
+
72
+ # GIVEN a test workflow that captures its context
73
+ class MyNode(BaseNode):
74
+ class Outputs(BaseOutputs):
75
+ generated_files: Any
76
+
77
+ def run(self) -> Outputs:
78
+ return self.Outputs(generated_files=self._context.generated_files)
79
+
80
+ class MyWorkflow(BaseWorkflow[BaseInputs, BaseState]):
81
+ graph = MyNode
82
+
83
+ class Outputs(BaseOutputs):
84
+ generated_files = MyNode.Outputs.generated_files
85
+
86
+ # GIVEN a tool router node
87
+ tool_router_node = create_tool_router_node(
88
+ ml_model="test-model",
89
+ blocks=[],
90
+ functions=[MyWorkflow],
91
+ prompt_inputs=None,
92
+ )
93
+
94
+ # WHEN we create a function node for the workflow
95
+ function_node_class = create_function_node(
96
+ function=MyWorkflow,
97
+ tool_router_node=tool_router_node,
98
+ )
99
+
100
+ # AND we create an instance with a context containing generated_files
101
+ function_node = function_node_class()
102
+
103
+ # Create a parent context with test data
104
+ parent_context = WorkflowContext(
105
+ generated_files={"script.py": "print('hello world')"},
106
+ )
107
+ function_node._context = parent_context
108
+
109
+ # Create a state with chat_history for the function node
110
+ class TestState(BaseState):
111
+ chat_history: List[ChatMessage] = []
112
+
113
+ function_node.state = TestState(meta=StateMeta(node_outputs={tool_router_node.Outputs.text: '{"arguments": {}}'}))
114
+
115
+ # WHEN the function node runs
116
+ outputs = function_node.run()
117
+
118
+ # THEN the workflow should have run successfully
119
+ assert outputs is not None
120
+
121
+ # AND the chat history should contain a function response
122
+ assert len(function_node.state.chat_history) == 1
123
+ function_response = function_node.state.chat_history[0]
124
+ assert function_response.role == "FUNCTION"
125
+
126
+ # AND the response should contain the generated files
127
+ assert isinstance(function_response.content, StringChatMessageContent)
128
+ data = json.loads(function_response.content.value)
129
+ assert data["generated_files"] == {"script.py": "print('hello world')"}
@@ -22,6 +22,7 @@ from vellum.workflows.outputs.base import BaseOutput
22
22
  from vellum.workflows.ports.port import Port
23
23
  from vellum.workflows.references.lazy import LazyReference
24
24
  from vellum.workflows.state.base import BaseState
25
+ from vellum.workflows.state.context import WorkflowContext
25
26
  from vellum.workflows.state.encoder import DefaultStateEncoder
26
27
  from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
27
28
  from vellum.workflows.types.generics import is_workflow_class
@@ -152,8 +153,7 @@ def create_function_node(
152
153
 
153
154
  # Call the function based on its type
154
155
  inputs_instance = function.get_inputs_class()(**arguments)
155
-
156
- workflow = function()
156
+ workflow = function(context=WorkflowContext.create_from(self._context))
157
157
  terminal_event = workflow.run(
158
158
  inputs=inputs_instance,
159
159
  )
@@ -8,7 +8,7 @@ if TYPE_CHECKING:
8
8
 
9
9
 
10
10
  class EnvironmentVariableReference(BaseDescriptor[str]):
11
- def __init__(self, *, name: str, default: Optional[str]):
11
+ def __init__(self, *, name: str, default: Optional[str] = None):
12
12
  super().__init__(name=name, types=(str,))
13
13
  self._default = default
14
14
 
@@ -20,5 +20,4 @@ class EnvironmentVariableReference(BaseDescriptor[str]):
20
20
  if self._default is not None:
21
21
  return self._default
22
22
 
23
- # Fetch Vellum Environment Variable named `self.name` once that project is done
24
- raise ValueError(f"No environment variable named '{self.name}' found")
23
+ return ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.68
3
+ Version: 0.14.70
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0