vellum-ai 1.2.5__py3-none-any.whl → 1.3.1__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 (48) hide show
  1. vellum/__init__.py +8 -0
  2. vellum/client/README.md +1 -1
  3. vellum/client/core/client_wrapper.py +2 -2
  4. vellum/client/reference.md +0 -9
  5. vellum/client/resources/workflow_sandboxes/client.py +0 -12
  6. vellum/client/resources/workflow_sandboxes/raw_client.py +2 -10
  7. vellum/client/types/__init__.py +8 -0
  8. vellum/client/types/deployment_read.py +5 -5
  9. vellum/client/types/slim_deployment_read.py +5 -5
  10. vellum/client/types/slim_workflow_deployment.py +5 -5
  11. vellum/client/types/workflow_deployment_read.py +5 -5
  12. vellum/client/types/workflow_request_audio_input_request.py +30 -0
  13. vellum/client/types/workflow_request_document_input_request.py +30 -0
  14. vellum/client/types/workflow_request_image_input_request.py +30 -0
  15. vellum/client/types/workflow_request_input_request.py +8 -0
  16. vellum/client/types/workflow_request_video_input_request.py +30 -0
  17. vellum/types/workflow_request_audio_input_request.py +3 -0
  18. vellum/types/workflow_request_document_input_request.py +3 -0
  19. vellum/types/workflow_request_image_input_request.py +3 -0
  20. vellum/types/workflow_request_video_input_request.py +3 -0
  21. vellum/workflows/events/types.py +6 -1
  22. vellum/workflows/events/workflow.py +9 -2
  23. vellum/workflows/integrations/tests/test_mcp_service.py +106 -1
  24. vellum/workflows/nodes/__init__.py +2 -0
  25. vellum/workflows/nodes/displayable/__init__.py +2 -0
  26. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +11 -0
  27. vellum/workflows/nodes/displayable/web_search_node/__init__.py +3 -0
  28. vellum/workflows/nodes/displayable/web_search_node/node.py +133 -0
  29. vellum/workflows/nodes/displayable/web_search_node/tests/__init__.py +0 -0
  30. vellum/workflows/nodes/displayable/web_search_node/tests/test_node.py +319 -0
  31. vellum/workflows/resolvers/base.py +3 -2
  32. vellum/workflows/resolvers/resolver.py +62 -7
  33. vellum/workflows/resolvers/tests/test_resolver.py +79 -7
  34. vellum/workflows/resolvers/types.py +11 -0
  35. vellum/workflows/runner/runner.py +49 -1
  36. vellum/workflows/state/context.py +41 -7
  37. vellum/workflows/utils/zip.py +46 -0
  38. vellum/workflows/workflows/base.py +10 -0
  39. {vellum_ai-1.2.5.dist-info → vellum_ai-1.3.1.dist-info}/METADATA +1 -1
  40. {vellum_ai-1.2.5.dist-info → vellum_ai-1.3.1.dist-info}/RECORD +48 -34
  41. vellum_cli/tests/test_init.py +7 -24
  42. vellum_cli/tests/test_pull.py +27 -52
  43. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +7 -33
  44. vellum_ee/workflows/display/utils/events.py +3 -0
  45. vellum_ee/workflows/tests/test_server.py +115 -0
  46. {vellum_ai-1.2.5.dist-info → vellum_ai-1.3.1.dist-info}/LICENSE +0 -0
  47. {vellum_ai-1.2.5.dist-info → vellum_ai-1.3.1.dist-info}/WHEEL +0 -0
  48. {vellum_ai-1.2.5.dist-info → vellum_ai-1.3.1.dist-info}/entry_points.txt +0 -0
vellum/__init__.py CHANGED
@@ -643,11 +643,15 @@ from .client.types import (
643
643
  WorkflowPushResponse,
644
644
  WorkflowReleaseTagRead,
645
645
  WorkflowReleaseTagWorkflowDeploymentHistoryItem,
646
+ WorkflowRequestAudioInputRequest,
646
647
  WorkflowRequestChatHistoryInputRequest,
648
+ WorkflowRequestDocumentInputRequest,
649
+ WorkflowRequestImageInputRequest,
647
650
  WorkflowRequestInputRequest,
648
651
  WorkflowRequestJsonInputRequest,
649
652
  WorkflowRequestNumberInputRequest,
650
653
  WorkflowRequestStringInputRequest,
654
+ WorkflowRequestVideoInputRequest,
651
655
  WorkflowResultEvent,
652
656
  WorkflowResultEventOutputData,
653
657
  WorkflowResultEventOutputDataArray,
@@ -1359,11 +1363,15 @@ __all__ = [
1359
1363
  "WorkflowPushResponse",
1360
1364
  "WorkflowReleaseTagRead",
1361
1365
  "WorkflowReleaseTagWorkflowDeploymentHistoryItem",
1366
+ "WorkflowRequestAudioInputRequest",
1362
1367
  "WorkflowRequestChatHistoryInputRequest",
1368
+ "WorkflowRequestDocumentInputRequest",
1369
+ "WorkflowRequestImageInputRequest",
1363
1370
  "WorkflowRequestInputRequest",
1364
1371
  "WorkflowRequestJsonInputRequest",
1365
1372
  "WorkflowRequestNumberInputRequest",
1366
1373
  "WorkflowRequestStringInputRequest",
1374
+ "WorkflowRequestVideoInputRequest",
1367
1375
  "WorkflowResultEvent",
1368
1376
  "WorkflowResultEventOutputData",
1369
1377
  "WorkflowResultEventOutputDataArray",
vellum/client/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fvellum-ai%2Fvellum-python-sdks)
4
4
  [![pypi](https://img.shields.io/pypi/v/vellum-ai)](https://pypi.python.org/pypi/vellum-ai)
5
5
 
6
- The Vellum Python library provides convenient access to the Vellum API from Python.
6
+ The Vellum Python library provides convenient access to the Vellum APIs from Python.
7
7
 
8
8
  ## API Docs
9
9
  You can find Vellum's complete API docs at [docs.vellum.ai](https://docs.vellum.ai/api-reference/introduction/getting-started).
@@ -27,10 +27,10 @@ class BaseClientWrapper:
27
27
 
28
28
  def get_headers(self) -> typing.Dict[str, str]:
29
29
  headers: typing.Dict[str, str] = {
30
- "User-Agent": "vellum-ai/1.2.5",
30
+ "User-Agent": "vellum-ai/1.3.1",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.2.5",
33
+ "X-Fern-SDK-Version": "1.3.1",
34
34
  **(self.get_custom_headers() or {}),
35
35
  }
36
36
  if self._api_version is not None:
@@ -6222,7 +6222,6 @@ client = Vellum(
6222
6222
  )
6223
6223
  client.workflow_sandboxes.deploy_workflow(
6224
6224
  id="id",
6225
- workflow_id="workflow_id",
6226
6225
  )
6227
6226
 
6228
6227
  ```
@@ -6247,14 +6246,6 @@ client.workflow_sandboxes.deploy_workflow(
6247
6246
  <dl>
6248
6247
  <dd>
6249
6248
 
6250
- **workflow_id:** `str` — An ID identifying the Workflow you'd like to deploy.
6251
-
6252
- </dd>
6253
- </dl>
6254
-
6255
- <dl>
6256
- <dd>
6257
-
6258
6249
  **workflow_deployment_id:** `typing.Optional[str]` — The Vellum-generated ID of the Workflow Deployment you'd like to update. Cannot specify both this and workflow_deployment_name. Leave null to create a new Workflow Deployment.
6259
6250
 
6260
6251
  </dd>
@@ -35,7 +35,6 @@ class WorkflowSandboxesClient:
35
35
  def deploy_workflow(
36
36
  self,
37
37
  id: str,
38
- workflow_id: str,
39
38
  *,
40
39
  workflow_deployment_id: typing.Optional[str] = OMIT,
41
40
  workflow_deployment_name: typing.Optional[str] = OMIT,
@@ -50,9 +49,6 @@ class WorkflowSandboxesClient:
50
49
  id : str
51
50
  A UUID string identifying this workflow sandbox.
52
51
 
53
- workflow_id : str
54
- An ID identifying the Workflow you'd like to deploy.
55
-
56
52
  workflow_deployment_id : typing.Optional[str]
57
53
  The Vellum-generated ID of the Workflow Deployment you'd like to update. Cannot specify both this and workflow_deployment_name. Leave null to create a new Workflow Deployment.
58
54
 
@@ -86,12 +82,10 @@ class WorkflowSandboxesClient:
86
82
  )
87
83
  client.workflow_sandboxes.deploy_workflow(
88
84
  id="id",
89
- workflow_id="workflow_id",
90
85
  )
91
86
  """
92
87
  _response = self._raw_client.deploy_workflow(
93
88
  id,
94
- workflow_id,
95
89
  workflow_deployment_id=workflow_deployment_id,
96
90
  workflow_deployment_name=workflow_deployment_name,
97
91
  label=label,
@@ -168,7 +162,6 @@ class AsyncWorkflowSandboxesClient:
168
162
  async def deploy_workflow(
169
163
  self,
170
164
  id: str,
171
- workflow_id: str,
172
165
  *,
173
166
  workflow_deployment_id: typing.Optional[str] = OMIT,
174
167
  workflow_deployment_name: typing.Optional[str] = OMIT,
@@ -183,9 +176,6 @@ class AsyncWorkflowSandboxesClient:
183
176
  id : str
184
177
  A UUID string identifying this workflow sandbox.
185
178
 
186
- workflow_id : str
187
- An ID identifying the Workflow you'd like to deploy.
188
-
189
179
  workflow_deployment_id : typing.Optional[str]
190
180
  The Vellum-generated ID of the Workflow Deployment you'd like to update. Cannot specify both this and workflow_deployment_name. Leave null to create a new Workflow Deployment.
191
181
 
@@ -224,7 +214,6 @@ class AsyncWorkflowSandboxesClient:
224
214
  async def main() -> None:
225
215
  await client.workflow_sandboxes.deploy_workflow(
226
216
  id="id",
227
- workflow_id="workflow_id",
228
217
  )
229
218
 
230
219
 
@@ -232,7 +221,6 @@ class AsyncWorkflowSandboxesClient:
232
221
  """
233
222
  _response = await self._raw_client.deploy_workflow(
234
223
  id,
235
- workflow_id,
236
224
  workflow_deployment_id=workflow_deployment_id,
237
225
  workflow_deployment_name=workflow_deployment_name,
238
226
  label=label,
@@ -24,7 +24,6 @@ class RawWorkflowSandboxesClient:
24
24
  def deploy_workflow(
25
25
  self,
26
26
  id: str,
27
- workflow_id: str,
28
27
  *,
29
28
  workflow_deployment_id: typing.Optional[str] = OMIT,
30
29
  workflow_deployment_name: typing.Optional[str] = OMIT,
@@ -39,9 +38,6 @@ class RawWorkflowSandboxesClient:
39
38
  id : str
40
39
  A UUID string identifying this workflow sandbox.
41
40
 
42
- workflow_id : str
43
- An ID identifying the Workflow you'd like to deploy.
44
-
45
41
  workflow_deployment_id : typing.Optional[str]
46
42
  The Vellum-generated ID of the Workflow Deployment you'd like to update. Cannot specify both this and workflow_deployment_name. Leave null to create a new Workflow Deployment.
47
43
 
@@ -66,7 +62,7 @@ class RawWorkflowSandboxesClient:
66
62
 
67
63
  """
68
64
  _response = self._client_wrapper.httpx_client.request(
69
- f"v1/workflow-sandboxes/{jsonable_encoder(id)}/workflows/{jsonable_encoder(workflow_id)}/deploy",
65
+ f"v1/workflow-sandboxes/{jsonable_encoder(id)}/deploy",
70
66
  base_url=self._client_wrapper.get_environment().default,
71
67
  method="POST",
72
68
  json={
@@ -165,7 +161,6 @@ class AsyncRawWorkflowSandboxesClient:
165
161
  async def deploy_workflow(
166
162
  self,
167
163
  id: str,
168
- workflow_id: str,
169
164
  *,
170
165
  workflow_deployment_id: typing.Optional[str] = OMIT,
171
166
  workflow_deployment_name: typing.Optional[str] = OMIT,
@@ -180,9 +175,6 @@ class AsyncRawWorkflowSandboxesClient:
180
175
  id : str
181
176
  A UUID string identifying this workflow sandbox.
182
177
 
183
- workflow_id : str
184
- An ID identifying the Workflow you'd like to deploy.
185
-
186
178
  workflow_deployment_id : typing.Optional[str]
187
179
  The Vellum-generated ID of the Workflow Deployment you'd like to update. Cannot specify both this and workflow_deployment_name. Leave null to create a new Workflow Deployment.
188
180
 
@@ -207,7 +199,7 @@ class AsyncRawWorkflowSandboxesClient:
207
199
 
208
200
  """
209
201
  _response = await self._client_wrapper.httpx_client.request(
210
- f"v1/workflow-sandboxes/{jsonable_encoder(id)}/workflows/{jsonable_encoder(workflow_id)}/deploy",
202
+ f"v1/workflow-sandboxes/{jsonable_encoder(id)}/deploy",
211
203
  base_url=self._client_wrapper.get_environment().default,
212
204
  method="POST",
213
205
  json={
@@ -667,11 +667,15 @@ from .workflow_push_exec_config import WorkflowPushExecConfig
667
667
  from .workflow_push_response import WorkflowPushResponse
668
668
  from .workflow_release_tag_read import WorkflowReleaseTagRead
669
669
  from .workflow_release_tag_workflow_deployment_history_item import WorkflowReleaseTagWorkflowDeploymentHistoryItem
670
+ from .workflow_request_audio_input_request import WorkflowRequestAudioInputRequest
670
671
  from .workflow_request_chat_history_input_request import WorkflowRequestChatHistoryInputRequest
672
+ from .workflow_request_document_input_request import WorkflowRequestDocumentInputRequest
673
+ from .workflow_request_image_input_request import WorkflowRequestImageInputRequest
671
674
  from .workflow_request_input_request import WorkflowRequestInputRequest
672
675
  from .workflow_request_json_input_request import WorkflowRequestJsonInputRequest
673
676
  from .workflow_request_number_input_request import WorkflowRequestNumberInputRequest
674
677
  from .workflow_request_string_input_request import WorkflowRequestStringInputRequest
678
+ from .workflow_request_video_input_request import WorkflowRequestVideoInputRequest
675
679
  from .workflow_result_event import WorkflowResultEvent
676
680
  from .workflow_result_event_output_data import WorkflowResultEventOutputData
677
681
  from .workflow_result_event_output_data_array import WorkflowResultEventOutputDataArray
@@ -1326,11 +1330,15 @@ __all__ = [
1326
1330
  "WorkflowPushResponse",
1327
1331
  "WorkflowReleaseTagRead",
1328
1332
  "WorkflowReleaseTagWorkflowDeploymentHistoryItem",
1333
+ "WorkflowRequestAudioInputRequest",
1329
1334
  "WorkflowRequestChatHistoryInputRequest",
1335
+ "WorkflowRequestDocumentInputRequest",
1336
+ "WorkflowRequestImageInputRequest",
1330
1337
  "WorkflowRequestInputRequest",
1331
1338
  "WorkflowRequestJsonInputRequest",
1332
1339
  "WorkflowRequestNumberInputRequest",
1333
1340
  "WorkflowRequestStringInputRequest",
1341
+ "WorkflowRequestVideoInputRequest",
1334
1342
  "WorkflowResultEvent",
1335
1343
  "WorkflowResultEventOutputData",
1336
1344
  "WorkflowResultEventOutputDataArray",
@@ -13,6 +13,10 @@ from .vellum_variable import VellumVariable
13
13
 
14
14
 
15
15
  class DeploymentRead(UniversalBaseModel):
16
+ """
17
+ A Prompt Deployment's full details.
18
+ """
19
+
16
20
  id: str
17
21
  created: dt.datetime
18
22
  label: str = pydantic.Field()
@@ -35,11 +39,7 @@ class DeploymentRead(UniversalBaseModel):
35
39
 
36
40
  environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
37
41
  """
38
- The environment this deployment is used in
39
-
40
- * `DEVELOPMENT` - Development
41
- * `STAGING` - Staging
42
- * `PRODUCTION` - Production
42
+ Deprecated. The value returned will always be 'PRODUCTION'.
43
43
  """
44
44
 
45
45
  last_deployed_on: dt.datetime
@@ -13,6 +13,10 @@ from .vellum_variable import VellumVariable
13
13
 
14
14
 
15
15
  class SlimDeploymentRead(UniversalBaseModel):
16
+ """
17
+ A subset of a Prompt Deployment's full details.
18
+ """
19
+
16
20
  id: str
17
21
  created: dt.datetime
18
22
  label: str = pydantic.Field()
@@ -35,11 +39,7 @@ class SlimDeploymentRead(UniversalBaseModel):
35
39
 
36
40
  environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
37
41
  """
38
- The environment this deployment is used in
39
-
40
- * `DEVELOPMENT` - Development
41
- * `STAGING` - Staging
42
- * `PRODUCTION` - Production
42
+ Deprecated. The value returned will always be 'PRODUCTION'.
43
43
  """
44
44
 
45
45
  last_deployed_on: dt.datetime
@@ -13,6 +13,10 @@ from .vellum_variable import VellumVariable
13
13
 
14
14
 
15
15
  class SlimWorkflowDeployment(UniversalBaseModel):
16
+ """
17
+ A subset of a Workflow Deployment's full details.
18
+ """
19
+
16
20
  id: str
17
21
  name: str = pydantic.Field()
18
22
  """
@@ -34,11 +38,7 @@ class SlimWorkflowDeployment(UniversalBaseModel):
34
38
 
35
39
  environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
36
40
  """
37
- The environment this workflow deployment is used in
38
-
39
- * `DEVELOPMENT` - Development
40
- * `STAGING` - Staging
41
- * `PRODUCTION` - Production
41
+ Deprecated. The value returned will always be 'PRODUCTION'.
42
42
  """
43
43
 
44
44
  created: dt.datetime
@@ -13,6 +13,10 @@ from .vellum_variable import VellumVariable
13
13
 
14
14
 
15
15
  class WorkflowDeploymentRead(UniversalBaseModel):
16
+ """
17
+ A Workflow Deployment's full details.
18
+ """
19
+
16
20
  id: str
17
21
  name: str = pydantic.Field()
18
22
  """
@@ -34,11 +38,7 @@ class WorkflowDeploymentRead(UniversalBaseModel):
34
38
 
35
39
  environment: typing.Optional[EnvironmentEnum] = pydantic.Field(default=None)
36
40
  """
37
- The environment this workflow deployment is used in
38
-
39
- * `DEVELOPMENT` - Development
40
- * `STAGING` - Staging
41
- * `PRODUCTION` - Production
41
+ Deprecated. The value returned will always be 'PRODUCTION'.
42
42
  """
43
43
 
44
44
  created: dt.datetime
@@ -0,0 +1,30 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .vellum_audio_request import VellumAudioRequest
8
+
9
+
10
+ class WorkflowRequestAudioInputRequest(UniversalBaseModel):
11
+ """
12
+ The input for an audio variable in a Workflow.
13
+ """
14
+
15
+ name: str = pydantic.Field()
16
+ """
17
+ The variable's name, as defined in the Workflow.
18
+ """
19
+
20
+ type: typing.Literal["AUDIO"] = "AUDIO"
21
+ value: VellumAudioRequest
22
+
23
+ if IS_PYDANTIC_V2:
24
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
25
+ else:
26
+
27
+ class Config:
28
+ frozen = True
29
+ smart_union = True
30
+ extra = pydantic.Extra.allow
@@ -0,0 +1,30 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .vellum_document_request import VellumDocumentRequest
8
+
9
+
10
+ class WorkflowRequestDocumentInputRequest(UniversalBaseModel):
11
+ """
12
+ The input for a document variable in a Workflow.
13
+ """
14
+
15
+ name: str = pydantic.Field()
16
+ """
17
+ The variable's name, as defined in the Workflow.
18
+ """
19
+
20
+ type: typing.Literal["DOCUMENT"] = "DOCUMENT"
21
+ value: VellumDocumentRequest
22
+
23
+ if IS_PYDANTIC_V2:
24
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
25
+ else:
26
+
27
+ class Config:
28
+ frozen = True
29
+ smart_union = True
30
+ extra = pydantic.Extra.allow
@@ -0,0 +1,30 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .vellum_image_request import VellumImageRequest
8
+
9
+
10
+ class WorkflowRequestImageInputRequest(UniversalBaseModel):
11
+ """
12
+ The input for an image variable in a Workflow.
13
+ """
14
+
15
+ name: str = pydantic.Field()
16
+ """
17
+ The variable's name, as defined in the Workflow.
18
+ """
19
+
20
+ type: typing.Literal["IMAGE"] = "IMAGE"
21
+ value: VellumImageRequest
22
+
23
+ if IS_PYDANTIC_V2:
24
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
25
+ else:
26
+
27
+ class Config:
28
+ frozen = True
29
+ smart_union = True
30
+ extra = pydantic.Extra.allow
@@ -2,14 +2,22 @@
2
2
 
3
3
  import typing
4
4
 
5
+ from .workflow_request_audio_input_request import WorkflowRequestAudioInputRequest
5
6
  from .workflow_request_chat_history_input_request import WorkflowRequestChatHistoryInputRequest
7
+ from .workflow_request_document_input_request import WorkflowRequestDocumentInputRequest
8
+ from .workflow_request_image_input_request import WorkflowRequestImageInputRequest
6
9
  from .workflow_request_json_input_request import WorkflowRequestJsonInputRequest
7
10
  from .workflow_request_number_input_request import WorkflowRequestNumberInputRequest
8
11
  from .workflow_request_string_input_request import WorkflowRequestStringInputRequest
12
+ from .workflow_request_video_input_request import WorkflowRequestVideoInputRequest
9
13
 
10
14
  WorkflowRequestInputRequest = typing.Union[
11
15
  WorkflowRequestStringInputRequest,
12
16
  WorkflowRequestJsonInputRequest,
13
17
  WorkflowRequestChatHistoryInputRequest,
14
18
  WorkflowRequestNumberInputRequest,
19
+ WorkflowRequestAudioInputRequest,
20
+ WorkflowRequestVideoInputRequest,
21
+ WorkflowRequestImageInputRequest,
22
+ WorkflowRequestDocumentInputRequest,
15
23
  ]
@@ -0,0 +1,30 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .vellum_video_request import VellumVideoRequest
8
+
9
+
10
+ class WorkflowRequestVideoInputRequest(UniversalBaseModel):
11
+ """
12
+ The input for a video variable in a Workflow.
13
+ """
14
+
15
+ name: str = pydantic.Field()
16
+ """
17
+ The variable's name, as defined in the Workflow.
18
+ """
19
+
20
+ type: typing.Literal["VIDEO"] = "VIDEO"
21
+ value: VellumVideoRequest
22
+
23
+ if IS_PYDANTIC_V2:
24
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
25
+ else:
26
+
27
+ class Config:
28
+ frozen = True
29
+ smart_union = True
30
+ extra = pydantic.Extra.allow
@@ -0,0 +1,3 @@
1
+ # WARNING: This file will be removed in a future release. Please import from "vellum.client" instead.
2
+
3
+ from vellum.client.types.workflow_request_audio_input_request 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.workflow_request_document_input_request 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.workflow_request_image_input_request 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.workflow_request_video_input_request import *
@@ -7,7 +7,6 @@ from pydantic import Field, GetCoreSchemaHandler, Tag, ValidationInfo
7
7
  from pydantic_core import CoreSchema, core_schema
8
8
 
9
9
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
10
- from vellum.client.types.span_link import SpanLink
11
10
  from vellum.workflows.state.encoder import DefaultStateEncoder
12
11
  from vellum.workflows.types.definition import VellumCodeResourceDefinition
13
12
  from vellum.workflows.types.utils import datetime_now
@@ -86,6 +85,12 @@ class ExternalParentContext(BaseParentContext):
86
85
  type: Literal["EXTERNAL"] = "EXTERNAL"
87
86
 
88
87
 
88
+ class SpanLink(UniversalBaseModel):
89
+ trace_id: str
90
+ type: Literal["TRIGGERED_BY", "PREVIOUS_SPAN", "ROOT_SPAN"]
91
+ span_context: "ParentContext"
92
+
93
+
89
94
  def _cast_parent_context_discriminator(v: Any) -> Any:
90
95
  if v in PARENT_CONTEXT_TYPES:
91
96
  return v
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from uuid import UUID
2
3
  from typing import TYPE_CHECKING, Any, Dict, Generic, Iterable, Literal, Optional, Type, Union
3
4
  from typing_extensions import TypeGuard
@@ -25,6 +26,8 @@ from .types import BaseEvent, default_serializer
25
26
  if TYPE_CHECKING:
26
27
  from vellum.workflows.workflows.base import BaseWorkflow
27
28
 
29
+ logger = logging.getLogger(__name__)
30
+
28
31
 
29
32
  class _BaseWorkflowExecutionBody(UniversalBaseModel):
30
33
  workflow_definition: Type["BaseWorkflow"]
@@ -107,8 +110,12 @@ class WorkflowExecutionInitiatedEvent(_BaseWorkflowEvent, Generic[InputsType, St
107
110
  ) -> WorkflowExecutionInitiatedBody[InputsType, StateType]:
108
111
  context = info.context if info and hasattr(info, "context") else {}
109
112
  if context and "event_enricher" in context and callable(context["event_enricher"]):
110
- event = context["event_enricher"](self)
111
- return event.body
113
+ try:
114
+ event = context["event_enricher"](self)
115
+ return event.body
116
+ except Exception as e:
117
+ logger.exception(f"Error in event_enricher: {e}")
118
+ return body
112
119
  else:
113
120
  return body
114
121
 
@@ -1,10 +1,11 @@
1
+ import pytest
1
2
  import asyncio
2
3
  import json
3
4
  from unittest import mock
4
5
 
5
6
  from vellum.workflows.constants import AuthorizationType
6
7
  from vellum.workflows.integrations.mcp_service import MCPHttpClient, MCPService
7
- from vellum.workflows.types.definition import MCPServer
8
+ from vellum.workflows.types.definition import MCPServer, MCPToolDefinition
8
9
 
9
10
 
10
11
  def test_mcp_http_client_sse_response():
@@ -118,3 +119,107 @@ def test_mcp_service_api_key_auth():
118
119
 
119
120
  # THEN the custom API key header should be set correctly
120
121
  assert headers == {"X-API-Key": "api-key-123"}
122
+
123
+
124
+ @pytest.mark.asyncio
125
+ async def test_mcp_http_client_empty_response():
126
+ """Test that empty responses are handled gracefully"""
127
+ # GIVEN a mock response that returns empty content
128
+ mock_response = mock.Mock()
129
+ mock_response.headers = {"content-type": "application/json"}
130
+ mock_response.text = ""
131
+
132
+ # AND a mock httpx client that returns this response
133
+ with mock.patch("vellum.workflows.integrations.mcp_service.httpx.AsyncClient") as mock_client_class:
134
+ mock_client = mock.AsyncMock()
135
+ mock_client.post.return_value = mock_response
136
+ mock_client_class.return_value = mock_client
137
+
138
+ # WHEN we call initialize with an empty response
139
+ # THEN it should raise an exception about empty response
140
+ async with MCPHttpClient("https://test.server.com", {}) as client:
141
+ with pytest.raises(Exception, match="Empty response received from server"):
142
+ await client.initialize()
143
+
144
+
145
+ @pytest.mark.asyncio
146
+ async def test_mcp_http_client_invalid_sse_json():
147
+ """Test that invalid JSON in SSE data is handled"""
148
+ # GIVEN an SSE response with invalid JSON
149
+ invalid_sse = """event: message
150
+ data: {invalid json}
151
+
152
+ """
153
+
154
+ mock_response = mock.Mock()
155
+ mock_response.headers = {"content-type": "text/event-stream"}
156
+ mock_response.text = invalid_sse
157
+
158
+ with mock.patch("vellum.workflows.integrations.mcp_service.httpx.AsyncClient") as mock_client_class:
159
+ mock_client = mock.AsyncMock()
160
+ mock_client.post.return_value = mock_response
161
+ mock_client_class.return_value = mock_client
162
+
163
+ # WHEN we call initialize with invalid SSE data
164
+ # THEN it should raise an exception about no valid JSON
165
+ async with MCPHttpClient("https://test.server.com", {}) as client:
166
+ with pytest.raises(Exception, match="No valid JSON data found in SSE response"):
167
+ await client.initialize()
168
+
169
+
170
+ def test_mcp_service_hydrate_tool_definitions():
171
+ """Test tool definition hydration with SSE responses"""
172
+ # GIVEN an MCP server configuration
173
+ sample_mcp_server = MCPServer(
174
+ name="test-server",
175
+ url="https://test.mcp.server.com/mcp",
176
+ authorization_type=AuthorizationType.BEARER_TOKEN,
177
+ bearer_token_value="test-token-123",
178
+ )
179
+
180
+ # AND a mock MCP service that returns tools via SSE
181
+ with mock.patch("vellum.workflows.integrations.mcp_service.asyncio.run") as mock_run:
182
+ mock_run.return_value = [
183
+ {
184
+ "name": "resolve-library-id",
185
+ "description": "Resolves library names to IDs",
186
+ "inputSchema": {
187
+ "type": "object",
188
+ "properties": {"libraryName": {"type": "string"}},
189
+ "required": ["libraryName"],
190
+ },
191
+ }
192
+ ]
193
+
194
+ # WHEN we hydrate tool definitions
195
+ service = MCPService()
196
+ tool_definitions = service.hydrate_tool_definitions(sample_mcp_server)
197
+
198
+ # THEN we should get properly formatted MCPToolDefinition objects
199
+ assert len(tool_definitions) == 1
200
+ assert isinstance(tool_definitions[0], MCPToolDefinition)
201
+ assert tool_definitions[0].name == "resolve-library-id"
202
+ assert tool_definitions[0].description == "Resolves library names to IDs"
203
+ assert tool_definitions[0].server == sample_mcp_server
204
+ assert tool_definitions[0].parameters == {
205
+ "type": "object",
206
+ "properties": {"libraryName": {"type": "string"}},
207
+ "required": ["libraryName"],
208
+ }
209
+
210
+
211
+ def test_mcp_service_list_tools_handles_errors():
212
+ """Test that SSE parsing errors are handled gracefully"""
213
+ # GIVEN an MCP server configuration
214
+ sample_mcp_server = MCPServer(name="test-server", url="https://test.mcp.server.com/mcp")
215
+
216
+ # AND a mock that raises an exception during SSE parsing
217
+ with mock.patch("vellum.workflows.integrations.mcp_service.asyncio.run") as mock_run:
218
+ mock_run.side_effect = Exception("SSE parsing failed")
219
+
220
+ # WHEN we try to list tools
221
+ service = MCPService()
222
+ tools = service.list_tools(sample_mcp_server)
223
+
224
+ # THEN we should get an empty list instead of crashing
225
+ assert tools == []