vellum-ai 0.14.52__py3-none-any.whl → 0.14.54__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/__init__.py CHANGED
@@ -133,15 +133,11 @@ class Vellum:
133
133
  self._client_wrapper = SyncClientWrapper(
134
134
  environment=environment,
135
135
  api_key=api_key,
136
- httpx_client=(
137
- httpx_client
138
- if httpx_client is not None
139
- else (
140
- httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects)
141
- if follow_redirects is not None
142
- else httpx.Client(timeout=_defaulted_timeout)
143
- )
144
- ),
136
+ httpx_client=httpx_client
137
+ if httpx_client is not None
138
+ else httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects)
139
+ if follow_redirects is not None
140
+ else httpx.Client(timeout=_defaulted_timeout),
145
141
  timeout=_defaulted_timeout,
146
142
  )
147
143
  self.ad_hoc = AdHocClient(client_wrapper=self._client_wrapper)
@@ -1446,9 +1442,7 @@ class Vellum:
1446
1442
  method="POST",
1447
1443
  json={
1448
1444
  "actuals": convert_and_respect_annotation_metadata(
1449
- object_=actuals,
1450
- annotation=typing.Sequence[SubmitWorkflowExecutionActualRequest],
1451
- direction="write",
1445
+ object_=actuals, annotation=typing.Sequence[SubmitWorkflowExecutionActualRequest], direction="write"
1452
1446
  ),
1453
1447
  "execution_id": execution_id,
1454
1448
  "external_id": external_id,
@@ -1515,15 +1509,11 @@ class AsyncVellum:
1515
1509
  self._client_wrapper = AsyncClientWrapper(
1516
1510
  environment=environment,
1517
1511
  api_key=api_key,
1518
- httpx_client=(
1519
- httpx_client
1520
- if httpx_client is not None
1521
- else (
1522
- httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects)
1523
- if follow_redirects is not None
1524
- else httpx.AsyncClient(timeout=_defaulted_timeout)
1525
- )
1526
- ),
1512
+ httpx_client=httpx_client
1513
+ if httpx_client is not None
1514
+ else httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects)
1515
+ if follow_redirects is not None
1516
+ else httpx.AsyncClient(timeout=_defaulted_timeout),
1527
1517
  timeout=_defaulted_timeout,
1528
1518
  )
1529
1519
  self.ad_hoc = AsyncAdHocClient(client_wrapper=self._client_wrapper)
@@ -2916,9 +2906,7 @@ class AsyncVellum:
2916
2906
  method="POST",
2917
2907
  json={
2918
2908
  "actuals": convert_and_respect_annotation_metadata(
2919
- object_=actuals,
2920
- annotation=typing.Sequence[SubmitWorkflowExecutionActualRequest],
2921
- direction="write",
2909
+ object_=actuals, annotation=typing.Sequence[SubmitWorkflowExecutionActualRequest], direction="write"
2922
2910
  ),
2923
2911
  "execution_id": execution_id,
2924
2912
  "external_id": external_id,
@@ -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.52",
21
+ "X-Fern-SDK-Version": "0.14.54",
22
22
  }
23
23
  headers["X-API-KEY"] = self.api_key
24
24
  return headers
@@ -69,7 +69,8 @@ class WorkflowsClient:
69
69
  try:
70
70
  if 200 <= _response.status_code < 300:
71
71
  _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
72
- yield from _response.iter_bytes(chunk_size=_chunk_size)
72
+ for _chunk in _response.iter_bytes(chunk_size=_chunk_size):
73
+ yield _chunk
73
74
  return
74
75
  _response.read()
75
76
  if _response.status_code == 400:
@@ -2,12 +2,13 @@ from dataclasses import field
2
2
  from functools import cached_property, reduce
3
3
  import inspect
4
4
  from types import MappingProxyType
5
- from uuid import UUID
5
+ from uuid import UUID, uuid4
6
6
  from typing import Any, Dict, Generic, Iterator, Optional, Set, Tuple, Type, TypeVar, Union, cast, get_args
7
7
 
8
8
  from vellum.workflows.constants import undefined
9
9
  from vellum.workflows.descriptors.base import BaseDescriptor
10
10
  from vellum.workflows.descriptors.utils import is_unresolved, resolve_value
11
+ from vellum.workflows.edges.edge import Edge
11
12
  from vellum.workflows.errors.types import WorkflowErrorCode
12
13
  from vellum.workflows.exceptions import NodeException
13
14
  from vellum.workflows.graph import Graph
@@ -325,6 +326,36 @@ class BaseNode(Generic[StateType], metaclass=BaseNodeMeta):
325
326
  code=WorkflowErrorCode.INVALID_INPUTS,
326
327
  )
327
328
 
329
+ @classmethod
330
+ def _queue_node_execution(
331
+ cls, state: StateType, dependencies: Set["Type[BaseNode]"], invoked_by: Optional[Edge] = None
332
+ ) -> UUID:
333
+ """
334
+ Queues a future execution of a node, returning the span id of the execution.
335
+
336
+ We may combine this into the should_initiate method, but we'll keep it separate for now to avoid
337
+ breaking changes until the 0.15.0 release.
338
+ """
339
+
340
+ execution_id = uuid4()
341
+ if not invoked_by:
342
+ return execution_id
343
+
344
+ if cls.merge_behavior not in {MergeBehavior.AWAIT_ANY, MergeBehavior.AWAIT_ALL}:
345
+ return execution_id
346
+
347
+ source_node = invoked_by.from_port.node_class
348
+ for queued_node_execution_id in state.meta.node_execution_cache._node_executions_queued[cls.node_class]:
349
+ if source_node not in state.meta.node_execution_cache._dependencies_invoked[queued_node_execution_id]:
350
+ state.meta.node_execution_cache._invoke_dependency(
351
+ queued_node_execution_id, cls.node_class, source_node, dependencies
352
+ )
353
+ return queued_node_execution_id
354
+
355
+ state.meta.node_execution_cache._node_executions_queued[cls.node_class].append(execution_id)
356
+ state.meta.node_execution_cache._invoke_dependency(execution_id, cls.node_class, source_node, dependencies)
357
+ return execution_id
358
+
328
359
  class Execution(metaclass=_BaseNodeExecutionMeta):
329
360
  node_class: Type["BaseNode"]
330
361
  count: int
@@ -11,7 +11,7 @@ from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePro
11
11
  from vellum.workflows.outputs.base import BaseOutput
12
12
  from vellum.workflows.ports.port import Port
13
13
  from vellum.workflows.references.lazy import LazyReference
14
- from vellum.workflows.types.core import EntityInputsInterface
14
+ from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
15
15
 
16
16
 
17
17
  class FunctionNode(BaseNode):
@@ -21,6 +21,9 @@ class FunctionNode(BaseNode):
21
21
 
22
22
 
23
23
  class ToolRouterNode(InlinePromptNode):
24
+ class Trigger(InlinePromptNode.Trigger):
25
+ merge_behavior = MergeBehavior.AWAIT_ATTRIBUTES
26
+
24
27
  def run(self) -> Iterator[BaseOutput]:
25
28
  self.prompt_inputs = {**self.prompt_inputs, "chat_history": self.state.chat_history} # type: ignore
26
29
  generator = super().run()
@@ -431,7 +431,7 @@ class WorkflowRunner(Generic[StateType]):
431
431
  return
432
432
 
433
433
  all_deps = self._dependencies[node_class]
434
- node_span_id = state.meta.node_execution_cache.queue_node_execution(node_class, all_deps, invoked_by)
434
+ node_span_id = node_class.Trigger._queue_node_execution(state, all_deps, invoked_by)
435
435
  if not node_class.Trigger.should_initiate(state, all_deps, node_span_id):
436
436
  return
437
437
 
@@ -16,7 +16,6 @@ from pydantic_core import core_schema
16
16
  from vellum.core.pydantic_utilities import UniversalBaseModel
17
17
  from vellum.utils.uuid import is_valid_uuid
18
18
  from vellum.workflows.constants import undefined
19
- from vellum.workflows.edges.edge import Edge
20
19
  from vellum.workflows.inputs.base import BaseInputs
21
20
  from vellum.workflows.references import ExternalInputReference, OutputReference, StateValueReference
22
21
  from vellum.workflows.types.definition import CodeResourceDefinition, serialize_type_encoder_with_id
@@ -174,23 +173,6 @@ class NodeExecutionCache:
174
173
  if all(dep in self._dependencies_invoked[execution_id] for dep in dependencies):
175
174
  self._node_executions_queued[node].remove(execution_id)
176
175
 
177
- def queue_node_execution(
178
- self, node: Type["BaseNode"], dependencies: Set["Type[BaseNode]"], invoked_by: Optional[Edge] = None
179
- ) -> UUID:
180
- execution_id = uuid4()
181
- if not invoked_by:
182
- return execution_id
183
-
184
- source_node = invoked_by.from_port.node_class
185
- for queued_node_execution_id in self._node_executions_queued[node]:
186
- if source_node not in self._dependencies_invoked[queued_node_execution_id]:
187
- self._invoke_dependency(queued_node_execution_id, node, source_node, dependencies)
188
- return queued_node_execution_id
189
-
190
- self._node_executions_queued[node].append(execution_id)
191
- self._invoke_dependency(execution_id, node, source_node, dependencies)
192
- return execution_id
193
-
194
176
  def is_node_execution_initiated(self, node: Type["BaseNode"], execution_id: UUID) -> bool:
195
177
  return execution_id in self._node_executions_initiated[node]
196
178
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.14.52
3
+ Version: 0.14.54
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -5,10 +5,10 @@ vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3
5
5
  vellum_cli/config.py,sha256=v5BmZ-t_v4Jmqd7KVuQMZF2pRI-rbMspSkVYXIRoTmI,9448
6
6
  vellum_cli/image_push.py,sha256=skFXf25ixMOX1yfcyAtii-RivYYv-_hsv-Z-bVB6m5Q,7380
7
7
  vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
8
- vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
8
+ vellum_cli/logger.py,sha256=p0OGmF6URHVpiIRjxtYiHIVlzxfwPjBqn8MW0C5FgsU,1486
9
9
  vellum_cli/ping.py,sha256=p_BCCRjgPhng6JktuECtkDQLbhopt6JpmrtGoLnLJT8,1161
10
10
  vellum_cli/pull.py,sha256=M50yXzA_35N35gk1Y8KjLbXrzdRG86--XFQvEukxGtA,13371
11
- vellum_cli/push.py,sha256=9oYmYhIWln3U0g7AstWEOA6ng5W_RthUA-Fie8FalFE,9846
11
+ vellum_cli/push.py,sha256=wxRlFu2mYW9SvwODYxwajri1mDQ2be0n-9i0d9QAc30,10194
12
12
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
14
14
  vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
@@ -17,7 +17,7 @@ vellum_cli/tests/test_init.py,sha256=8UOc_ThfouR4ja5cCl_URuLk7ohr9JXfCnG4yka1OUQ
17
17
  vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
18
18
  vellum_cli/tests/test_ping.py,sha256=3ucVRThEmTadlV9LrJdCCrr1Ofj3rOjG6ue0BNR2UC0,2523
19
19
  vellum_cli/tests/test_pull.py,sha256=7HRAhIdkVW5mR2VckEaNDjp4rt-MlIxOWMMI2XNUPE8,49814
20
- vellum_cli/tests/test_push.py,sha256=K-TaOjU4mc-x0-ee1DNXT7yZBC0pEM-R9VY57kdMdmY,32849
20
+ vellum_cli/tests/test_push.py,sha256=I8XICg3pUb3yxAFLXziVHHf5CRm354LO-uUfwtca3bU,33897
21
21
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -100,7 +100,7 @@ vellum_ee/workflows/display/utils/registry.py,sha256=fWIm5Jj-10gNFjgn34iBu4RWv3V
100
100
  vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
101
101
  vellum_ee/workflows/display/vellum.py,sha256=o7mq_vk2Yapu9DDKRz5l76h8EmCAypWGQYe6pryrbB8,3576
102
102
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
103
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=DbjLChDtlNAs86sWL5-ojYYzwFsOjACAGfquxM3VYcw,32563
103
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=dVOe3TOV00uvllxsDziM3pFfL2HYTkWRq9iKDlj_xyU,33162
104
104
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
105
105
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=rRwXLgsXqiaSn3jzP7lc--pytRW3Jmnj2-zNq5l-FQ4,29472
106
106
  vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=aaKdmWrgEe5YyV4zuDY_4E3y-l59rIHQnNGiPj2OWxQ,359
@@ -125,15 +125,15 @@ vellum_ee/workflows/tests/local_workflow/nodes/__init__.py,sha256=1F6jxUpSKfPXPj
125
125
  vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zirg0w9VKUW3QVDSdBLDqcqAMZjCL_oWbpU,297
126
126
  vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
127
127
  vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
128
- vellum_ee/workflows/tests/test_display_meta.py,sha256=C25dErwghPNXio49pvSRxyOuc96srH6eYEwTAWdE2zY,2258
128
+ vellum_ee/workflows/tests/test_display_meta.py,sha256=DIzjNbwK1-4mlttPML6NskQ4rPVMXhj5zeOmBEyPqKI,3728
129
129
  vellum_ee/workflows/tests/test_server.py,sha256=SsOkS6sGO7uGC4mxvk4iv8AtcXs058P9hgFHzTWmpII,14519
130
130
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
131
131
  vellum/__init__.py,sha256=Hqfl49WZJzzqOKzVsTGi-j9twIqFOoRmACJsrEsjL44,41918
132
132
  vellum/client/README.md,sha256=qmaVIP42MnxAu8jV7u-CsgVFfs3-pHQODrXdZdFxtaw,4749
133
- vellum/client/__init__.py,sha256=nv_MItkRFOTsTDcray01bea7NvO-P9bAj8lnUfTbxOo,120440
133
+ vellum/client/__init__.py,sha256=PEnFl7LbXQcvAi3bVN2qyt5xm2FtVtq7xWKkcWM3Tg4,120166
134
134
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
135
135
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
136
- vellum/client/core/client_wrapper.py,sha256=P667a77GUeHTNshBFAxTS1VkSHNO_joyM4HtqkCS-8o,1869
136
+ vellum/client/core/client_wrapper.py,sha256=kZK3hw4JmWRWNhWYDC2kTCPv6kwNoNAA0GRUJt89dT8,1869
137
137
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
138
138
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
139
139
  vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
@@ -196,7 +196,7 @@ vellum/client/resources/workflow_sandboxes/client.py,sha256=XfMcbvSTF1_iTGIXsk1F
196
196
  vellum/client/resources/workflow_sandboxes/types/__init__.py,sha256=EaGVRU1w6kJiiHrbZOeEa0c3ggjfgv_jBqsyOkCRWOI,212
197
197
  vellum/client/resources/workflow_sandboxes/types/list_workflow_sandbox_examples_request_tag.py,sha256=TEwWit20W3X-zWPPLAhmUG05UudG9gaBSJ4Q4-rNJws,188
198
198
  vellum/client/resources/workflows/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
199
- vellum/client/resources/workflows/client.py,sha256=OwpMojUEZ6DdtqW5Q-165SCthYFbzt3IBVKHR5-4h-0,11244
199
+ vellum/client/resources/workflows/client.py,sha256=uDC61aybVmgxPiLKuLpAB-fK3sagnFFX06zzmQngInA,11285
200
200
  vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
201
201
  vellum/client/resources/workspace_secrets/client.py,sha256=zlBdbeTP6sqvtyl_DlrpfG-W5hSP7tJ1NYLSygi4CLU,8205
202
202
  vellum/client/resources/workspaces/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
@@ -1543,7 +1543,7 @@ vellum/workflows/inputs/tests/test_inputs.py,sha256=lioA8917mFLYq7Ml69UNkqUjcWbb
1543
1543
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1544
1544
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1545
1545
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1546
- vellum/workflows/nodes/bases/base.py,sha256=lQOUhVIgZMuYo6Kd5L7mcBooAVYERWjDL3csrAMP5Pg,15626
1546
+ vellum/workflows/nodes/bases/base.py,sha256=3yWWY6ZYpUIMzGyKeu_7xs8qC58uef0ly6EVUbWIAQ0,17170
1547
1547
  vellum/workflows/nodes/bases/base_adornment_node.py,sha256=Ao2opOW4kgNoYXFF9Pk7IMpVZdy6luwrjcqEwU5Q9V0,3404
1548
1548
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1549
1549
  vellum/workflows/nodes/bases/tests/test_base_adornment_node.py,sha256=fXZI9KqpS4XMBrBnIEkK3foHaBVvyHwYcQWWDKay7ic,1148
@@ -1641,7 +1641,7 @@ vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=c
1641
1641
  vellum/workflows/nodes/experimental/tool_calling_node/__init__.py,sha256=S7OzT3I4cyOU5Beoz87nPwCejCMP2FsHBFL8OcVmxJ4,118
1642
1642
  vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=NUC7VZj2D86IDQzjCq_a3-Xeqj_b3BE8T1kOMIfN7V8,4878
1643
1643
  vellum/workflows/nodes/experimental/tool_calling_node/tests/test_tool_calling_node.py,sha256=sxG26mOwt4N36RLoPJ-ngginPqC5qFzD_kGj9izdCFI,1833
1644
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=cdFR0yeb0mDl5CmH27cYQWIb4STg-ZfqtuI6rW66AHo,5097
1644
+ vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=mIQpAxLT2IF4SNQvUpRsX-t0jODxQ3Xvb_LEF7bRMF8,5214
1645
1645
  vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
1646
1646
  vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1647
1647
  vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
@@ -1669,10 +1669,10 @@ vellum/workflows/references/workflow_input.py,sha256=W3rOK1EPd2gYHb04WJwmNm1CUSd
1669
1669
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1670
1670
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1671
1671
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1672
- vellum/workflows/runner/runner.py,sha256=BOFqcxvK2oktJmcEIdHBaSanZtWcaqJq06s9aLQFaZw,33000
1672
+ vellum/workflows/runner/runner.py,sha256=0Ufxyl3nwWVI--w_iVTAVXUOKJXuZiEn6-BrrUAGVBs,32983
1673
1673
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1674
1674
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1675
- vellum/workflows/state/base.py,sha256=ZXDmVafs6sExcbx1azrZjEGQsmuY68mSRWfI7t2PT4c,22330
1675
+ vellum/workflows/state/base.py,sha256=-0b-nNBEXvGVau4c1BUwmCsXfo5wZD5VjLb8-eqi0Y8,21502
1676
1676
  vellum/workflows/state/context.py,sha256=KOAI1wEGn8dGmhmAemJaf4SZbitP3jpIBcwKfznQaRE,3076
1677
1677
  vellum/workflows/state/encoder.py,sha256=z7Mk6jQC-92wCj6XTK7VEnJ8px_lU8qy0BINqwGDN4I,2063
1678
1678
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
@@ -1707,8 +1707,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
1707
1707
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1708
1708
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=8P5YIsNMO78_CR1NNK6wkEdkMB4b3Q_Ni1qxh78OnHo,20481
1709
1709
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1710
- vellum_ai-0.14.52.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1711
- vellum_ai-0.14.52.dist-info/METADATA,sha256=-vGzZDBmw_wd9r-qwKB7WAO8eJvsTB0_OGcEFjPwVU0,5484
1712
- vellum_ai-0.14.52.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1713
- vellum_ai-0.14.52.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1714
- vellum_ai-0.14.52.dist-info/RECORD,,
1710
+ vellum_ai-0.14.54.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1711
+ vellum_ai-0.14.54.dist-info/METADATA,sha256=uwF0Sw7FcZnvFQcsUZvPH5POXx6DlpH16vKfUlk4DdQ,5484
1712
+ vellum_ai-0.14.54.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1713
+ vellum_ai-0.14.54.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1714
+ vellum_ai-0.14.54.dist-info/RECORD,,
vellum_cli/logger.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import os
3
+ from typing import Optional
3
4
 
4
5
 
5
6
  class CLIFormatter(logging.Formatter):
@@ -34,3 +35,14 @@ def load_cli_logger() -> logging.Logger:
34
35
  logger.addHandler(handler)
35
36
 
36
37
  return logger
38
+
39
+
40
+ def handle_cli_error(logger: logging.Logger, title: str, message: str, suggestion: Optional[str] = None):
41
+ logger.error("\n\033[1;31m✖ {}\033[0m".format(title)) # Bold red X and title
42
+ logger.error("\n\033[1m{}\033[0m".format(message)) # Bold message
43
+ if suggestion:
44
+ logger.error("\n\033[1;34mNext steps:\033[0m") # Bold blue
45
+ logger.error(suggestion)
46
+
47
+ logger.error(f"{title}: {message}")
48
+ exit(1)
vellum_cli/push.py CHANGED
@@ -15,7 +15,7 @@ from vellum.types import WorkflowPushDeploymentConfigRequest
15
15
  from vellum.workflows.vellum_client import create_vellum_client
16
16
  from vellum.workflows.workflows.base import BaseWorkflow
17
17
  from vellum_cli.config import DEFAULT_WORKSPACE_CONFIG, WorkflowConfig, WorkflowDeploymentConfig, load_vellum_cli_config
18
- from vellum_cli.logger import load_cli_logger
18
+ from vellum_cli.logger import handle_cli_error, load_cli_logger
19
19
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
20
20
 
21
21
 
@@ -178,41 +178,39 @@ def push_command(
178
178
  strict=strict,
179
179
  )
180
180
  except ApiError as e:
181
- if e.status_code != 400 or not isinstance(e.body, dict) or "diffs" not in e.body:
182
- raise e
183
-
184
- diffs: dict = e.body["diffs"]
185
- generated_only = diffs.get("generated_only", [])
186
- generated_only_str = (
187
- "\n".join(
188
- ["Files that were generated but not found in the original project:"]
189
- + [f"- {file}" for file in generated_only]
181
+ if e.status_code == 400 and isinstance(e.body, dict) and "diffs" in e.body:
182
+ diffs: dict = e.body["diffs"]
183
+ generated_only = diffs.get("generated_only", [])
184
+ generated_only_str = (
185
+ "\n".join(
186
+ ["Files that were generated but not found in the original project:"]
187
+ + [f"- {file}" for file in generated_only]
188
+ )
189
+ if generated_only
190
+ else ""
190
191
  )
191
- if generated_only
192
- else ""
193
- )
194
192
 
195
- original_only = diffs.get("original_only", [])
196
- original_only_str = (
197
- "\n".join(
198
- ["Files that were found in the original project but not generated:"]
199
- + [f"- {file}" for file in original_only]
193
+ original_only = diffs.get("original_only", [])
194
+ original_only_str = (
195
+ "\n".join(
196
+ ["Files that were found in the original project but not generated:"]
197
+ + [f"- {file}" for file in original_only]
198
+ )
199
+ if original_only
200
+ else ""
200
201
  )
201
- if original_only
202
- else ""
203
- )
204
202
 
205
- modified = diffs.get("modified", {})
206
- modified_str = (
207
- "\n\n".join(
208
- ["Files that were different between the original project and the generated artifact:"]
209
- + ["\n".join(line.strip() for line in lines) for lines in modified.values()]
203
+ modified = diffs.get("modified", {})
204
+ modified_str = (
205
+ "\n\n".join(
206
+ ["Files that were different between the original project and the generated artifact:"]
207
+ + ["\n".join(line.strip() for line in lines) for lines in modified.values()]
208
+ )
209
+ if modified
210
+ else ""
210
211
  )
211
- if modified
212
- else ""
213
- )
214
212
 
215
- reported_diffs = f"""\
213
+ reported_diffs = f"""\
216
214
  {e.body.get("detail")}
217
215
 
218
216
  {generated_only_str}
@@ -221,8 +219,14 @@ def push_command(
221
219
 
222
220
  {modified_str}
223
221
  """
224
- logger.error(reported_diffs)
225
- return
222
+ logger.error(reported_diffs)
223
+ return
224
+
225
+ if e.status_code == 400 and isinstance(e.body, dict) and "detail" in e.body:
226
+ handle_cli_error(logger, title="API request to /workflows/push failed.", message=e.body["detail"])
227
+ return
228
+
229
+ raise e
226
230
 
227
231
  if dry_run:
228
232
  error_messages = [str(e) for e in workflow_display.errors]
@@ -525,6 +525,37 @@ Files that were different between the original project and the generated artifac
525
525
  )
526
526
 
527
527
 
528
+ def test_push__push_fails_due_to_400_error(mock_module, vellum_client):
529
+ # GIVEN a single workflow configured
530
+ temp_dir = mock_module.temp_dir
531
+ module = mock_module.module
532
+
533
+ # AND a workflow exists in the module successfully
534
+ _ensure_workflow_py(temp_dir, module)
535
+
536
+ # AND the push API call returns a 4xx response
537
+ vellum_client.workflows.push.side_effect = ApiError(
538
+ status_code=400,
539
+ body={
540
+ "detail": "Pushing the Workflow failed because you did something wrong",
541
+ },
542
+ )
543
+
544
+ # WHEN calling `vellum push` on strict mode
545
+ runner = CliRunner()
546
+ result = runner.invoke(cli_main, ["push", module])
547
+
548
+ # THEN it should fail with a user error code
549
+ assert result.exit_code == 1
550
+
551
+ # AND the error message should be in the error message
552
+ assert "API request to /workflows/push failed." in result.output
553
+ assert "Pushing the Workflow failed because you did something wrong" in result.output
554
+
555
+ # AND the stack trace should not be
556
+ assert "Traceback" not in result.output
557
+
558
+
528
559
  @pytest.mark.parametrize(
529
560
  "file_data",
530
561
  [
@@ -583,14 +583,30 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
583
583
  # DEPRECATED: This will be removed in the 0.15.0 release
584
584
  workflow_class: Optional[Type[BaseWorkflow]] = None,
585
585
  ) -> Union[WorkflowEventDisplayContext, None]:
586
- workflow_display_module = f"{module_path}.display.workflow"
586
+ full_workflow_display_module_path = f"{module_path}.display.workflow"
587
587
  try:
588
- display_class = importlib.import_module(workflow_display_module)
588
+ display_module = importlib.import_module(full_workflow_display_module_path)
589
589
  except ModuleNotFoundError:
590
+ logger.exception("Failed to import workflow display module: %s", full_workflow_display_module_path)
590
591
  return None
591
592
 
592
- WorkflowDisplayClass = display_class.WorkflowDisplay
593
- if not isinstance(WorkflowDisplayClass, type) or not issubclass(WorkflowDisplayClass, BaseWorkflowDisplay):
593
+ WorkflowDisplayClass: Optional[Type[BaseWorkflowDisplay]] = None
594
+ for name, definition in display_module.__dict__.items():
595
+ if name.startswith("_"):
596
+ continue
597
+
598
+ if (
599
+ not isinstance(definition, type)
600
+ or not issubclass(definition, BaseWorkflowDisplay)
601
+ or definition == BaseWorkflowDisplay
602
+ ):
603
+ continue
604
+
605
+ WorkflowDisplayClass = definition
606
+ break
607
+
608
+ if not WorkflowDisplayClass:
609
+ logger.exception("No workflow display class found in module: %s", full_workflow_display_module_path)
594
610
  return None
595
611
 
596
612
  return WorkflowDisplayClass().get_event_display_context()
@@ -54,3 +54,51 @@ def test_base_class_dynamic_import(files):
54
54
  }
55
55
  assert display_meta
56
56
  assert display_meta.model_dump(mode="json") == expected_result
57
+
58
+
59
+ def test_gather_event_display_context__custom_workflow_name():
60
+ # GIVEN a workflow module with a custom workflow name
61
+ workflow_output_id = uuid4()
62
+ files = {
63
+ "__init__.py": "",
64
+ "workflow.py": """\
65
+ from vellum.workflows import BaseWorkflow
66
+
67
+ class MyCustomWorkflow(BaseWorkflow):
68
+ class Outputs(BaseWorkflow.Outputs):
69
+ answer = "foo"
70
+ """,
71
+ "display/__init__.py": """\
72
+ # flake8: noqa: F401, F403
73
+
74
+ from .workflow import *
75
+ """,
76
+ "display/workflow.py": f"""\
77
+ from uuid import UUID
78
+ from vellum_ee.workflows.display.workflows import BaseWorkflowDisplay
79
+ from vellum_ee.workflows.display.base import WorkflowOutputDisplay
80
+ from ..workflow import MyCustomWorkflow
81
+
82
+ class MyCustomWorkflowDisplay(BaseWorkflowDisplay[MyCustomWorkflow]):
83
+ output_displays = {{
84
+ MyCustomWorkflow.Outputs.answer: WorkflowOutputDisplay(
85
+ id=UUID("{workflow_output_id}"), name="answer"
86
+ )
87
+ }}
88
+ """,
89
+ }
90
+
91
+ namespace = str(uuid4())
92
+
93
+ # AND the virtual file loader is registered
94
+ sys.meta_path.append(VirtualFileFinder(files, namespace))
95
+
96
+ # WHEN the workflow display context is gathered
97
+ Workflow = BaseWorkflow.load_from_module(namespace)
98
+ display_meta = BaseWorkflowDisplay.gather_event_display_context(namespace, Workflow)
99
+
100
+ # THEN the workflow display context is successfully gathered
101
+ assert display_meta
102
+ assert display_meta.workflow_outputs == {
103
+ "answer": workflow_output_id,
104
+ }