vellum-ai 0.11.1__py3-none-any.whl → 0.11.4__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 (30) hide show
  1. vellum/__init__.py +2 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +2 -0
  4. vellum/client/types/document_document_to_document_index.py +6 -0
  5. vellum/client/types/slim_document.py +2 -2
  6. vellum/client/types/slim_document_document_to_document_index.py +43 -0
  7. vellum/types/slim_document_document_to_document_index.py +3 -0
  8. vellum/workflows/descriptors/base.py +1 -1
  9. vellum/workflows/graph/graph.py +23 -4
  10. vellum/workflows/graph/tests/test_graph.py +25 -0
  11. vellum/workflows/nodes/bases/tests/test_base_node.py +1 -1
  12. vellum/workflows/nodes/core/map_node/node.py +27 -4
  13. vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +21 -0
  14. vellum/workflows/ports/port.py +3 -0
  15. {vellum_ai-0.11.1.dist-info → vellum_ai-0.11.4.dist-info}/METADATA +1 -1
  16. {vellum_ai-0.11.1.dist-info → vellum_ai-0.11.4.dist-info}/RECORD +30 -28
  17. vellum_cli/config.py +36 -0
  18. vellum_cli/push.py +4 -0
  19. vellum_cli/tests/conftest.py +19 -4
  20. vellum_cli/tests/test_pull.py +85 -7
  21. vellum_cli/tests/test_push.py +8 -8
  22. vellum_ee/workflows/display/base.py +1 -3
  23. vellum_ee/workflows/display/nodes/vellum/merge_node.py +20 -1
  24. vellum_ee/workflows/display/nodes/vellum/templating_node.py +15 -4
  25. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +2 -2
  26. vellum_ee/workflows/display/vellum.py +3 -3
  27. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +8 -2
  28. {vellum_ai-0.11.1.dist-info → vellum_ai-0.11.4.dist-info}/LICENSE +0 -0
  29. {vellum_ai-0.11.1.dist-info → vellum_ai-0.11.4.dist-info}/WHEEL +0 -0
  30. {vellum_ai-0.11.1.dist-info → vellum_ai-0.11.4.dist-info}/entry_points.txt +0 -0
vellum/__init__.py CHANGED
@@ -319,6 +319,7 @@ from .types import (
319
319
  SentenceChunkingRequest,
320
320
  SlimDeploymentRead,
321
321
  SlimDocument,
322
+ SlimDocumentDocumentToDocumentIndex,
322
323
  SlimWorkflowDeployment,
323
324
  StreamingAdHocExecutePromptEvent,
324
325
  StreamingExecutePromptEvent,
@@ -851,6 +852,7 @@ __all__ = [
851
852
  "SentenceChunkingRequest",
852
853
  "SlimDeploymentRead",
853
854
  "SlimDocument",
855
+ "SlimDocumentDocumentToDocumentIndex",
854
856
  "SlimWorkflowDeployment",
855
857
  "StreamingAdHocExecutePromptEvent",
856
858
  "StreamingExecutePromptEvent",
@@ -17,7 +17,7 @@ class BaseClientWrapper:
17
17
  headers: typing.Dict[str, str] = {
18
18
  "X-Fern-Language": "Python",
19
19
  "X-Fern-SDK-Name": "vellum-ai",
20
- "X-Fern-SDK-Version": "0.11.1",
20
+ "X-Fern-SDK-Version": "0.11.4",
21
21
  }
22
22
  headers["X_API_KEY"] = self.api_key
23
23
  return headers
@@ -330,6 +330,7 @@ from .sentence_chunking import SentenceChunking
330
330
  from .sentence_chunking_request import SentenceChunkingRequest
331
331
  from .slim_deployment_read import SlimDeploymentRead
332
332
  from .slim_document import SlimDocument
333
+ from .slim_document_document_to_document_index import SlimDocumentDocumentToDocumentIndex
333
334
  from .slim_workflow_deployment import SlimWorkflowDeployment
334
335
  from .streaming_ad_hoc_execute_prompt_event import StreamingAdHocExecutePromptEvent
335
336
  from .streaming_execute_prompt_event import StreamingExecutePromptEvent
@@ -839,6 +840,7 @@ __all__ = [
839
840
  "SentenceChunkingRequest",
840
841
  "SlimDeploymentRead",
841
842
  "SlimDocument",
843
+ "SlimDocumentDocumentToDocumentIndex",
842
844
  "SlimWorkflowDeployment",
843
845
  "StreamingAdHocExecutePromptEvent",
844
846
  "StreamingExecutePromptEvent",
@@ -8,6 +8,10 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2
8
8
 
9
9
 
10
10
  class DocumentDocumentToDocumentIndex(UniversalBaseModel):
11
+ """
12
+ A detailed representation of the link between a Document and a Document Index it's a member of.
13
+ """
14
+
11
15
  id: str = pydantic.Field()
12
16
  """
13
17
  Vellum-generated ID that uniquely identifies this link.
@@ -29,6 +33,8 @@ class DocumentDocumentToDocumentIndex(UniversalBaseModel):
29
33
  - `FAILED` - Failed
30
34
  """
31
35
 
36
+ extracted_text_file_url: typing.Optional[str] = None
37
+
32
38
  if IS_PYDANTIC_V2:
33
39
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
34
40
  else:
@@ -7,7 +7,7 @@ import datetime as dt
7
7
  from .document_processing_state import DocumentProcessingState
8
8
  from .processing_failure_reason_enum import ProcessingFailureReasonEnum
9
9
  from .document_status import DocumentStatus
10
- from .document_document_to_document_index import DocumentDocumentToDocumentIndex
10
+ from .slim_document_document_to_document_index import SlimDocumentDocumentToDocumentIndex
11
11
  from ..core.pydantic_utilities import IS_PYDANTIC_V2
12
12
 
13
13
 
@@ -58,7 +58,7 @@ class SlimDocument(UniversalBaseModel):
58
58
  A previously supplied JSON object containing metadata that can be filtered on when searching.
59
59
  """
60
60
 
61
- document_to_document_indexes: typing.List[DocumentDocumentToDocumentIndex]
61
+ document_to_document_indexes: typing.List[SlimDocumentDocumentToDocumentIndex]
62
62
 
63
63
  if IS_PYDANTIC_V2:
64
64
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -0,0 +1,43 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from ..core.pydantic_utilities import UniversalBaseModel
4
+ import pydantic
5
+ import typing
6
+ from .indexing_state_enum import IndexingStateEnum
7
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2
8
+
9
+
10
+ class SlimDocumentDocumentToDocumentIndex(UniversalBaseModel):
11
+ """
12
+ A slim representation of the link between a Document and a Document Index it's a member of.
13
+ """
14
+
15
+ id: str = pydantic.Field()
16
+ """
17
+ Vellum-generated ID that uniquely identifies this link.
18
+ """
19
+
20
+ document_index_id: str = pydantic.Field()
21
+ """
22
+ Vellum-generated ID that uniquely identifies the index this document is included in.
23
+ """
24
+
25
+ indexing_state: typing.Optional[IndexingStateEnum] = pydantic.Field(default=None)
26
+ """
27
+ An enum value representing where this document is along its indexing lifecycle for this index.
28
+
29
+ - `AWAITING_PROCESSING` - Awaiting Processing
30
+ - `QUEUED` - Queued
31
+ - `INDEXING` - Indexing
32
+ - `INDEXED` - Indexed
33
+ - `FAILED` - Failed
34
+ """
35
+
36
+ if IS_PYDANTIC_V2:
37
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
38
+ else:
39
+
40
+ class Config:
41
+ frozen = True
42
+ smart_union = True
43
+ 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.slim_document_document_to_document_index import *
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
30
30
  from vellum.workflows.nodes.bases import BaseNode
31
31
  from vellum.workflows.state.base import BaseState
32
32
 
33
- _T = TypeVar("_T")
33
+ _T = TypeVar("_T", covariant=True)
34
34
  _O = TypeVar("_O")
35
35
  _O2 = TypeVar("_O2")
36
36
 
@@ -12,11 +12,13 @@ if TYPE_CHECKING:
12
12
  GraphTargetOfSets = Union[
13
13
  Set[NodeType],
14
14
  Set["Graph"],
15
- Set[Union[Type["BaseNode"], "Graph"]],
15
+ Set["Port"],
16
+ Set[Union[Type["BaseNode"], "Graph", "Port"]],
16
17
  ]
17
18
 
18
19
  GraphTarget = Union[
19
20
  Type["BaseNode"],
21
+ "Port",
20
22
  "Graph",
21
23
  GraphTargetOfSets,
22
24
  ]
@@ -53,9 +55,13 @@ class Graph:
53
55
  entrypoints.update(target._entrypoints)
54
56
  edges.update(target._edges)
55
57
  terminals.update(target._terminals)
56
- else:
58
+ elif hasattr(target, "Ports"):
57
59
  entrypoints.update({port for port in target.Ports})
58
60
  terminals.update({port for port in target.Ports})
61
+ else:
62
+ # target is a Port
63
+ entrypoints.update({target})
64
+ terminals.update({target})
59
65
 
60
66
  return Graph(entrypoints=entrypoints, edges=list(edges), terminals=terminals)
61
67
 
@@ -77,11 +83,16 @@ class Graph:
77
83
  self._extend_edges(elem.edges)
78
84
  for other_terminal in elem._terminals:
79
85
  new_terminals.add(other_terminal)
80
- else:
86
+ elif hasattr(elem, "Ports"):
81
87
  midgraph = final_output_node >> elem
82
88
  self._extend_edges(midgraph.edges)
83
89
  for other_terminal in elem.Ports:
84
90
  new_terminals.add(other_terminal)
91
+ else:
92
+ # elem is a Port
93
+ midgraph = final_output_node >> elem
94
+ self._extend_edges(midgraph.edges)
95
+ new_terminals.add(elem)
85
96
  self._terminals = new_terminals
86
97
  return self
87
98
 
@@ -93,10 +104,18 @@ class Graph:
93
104
  self._terminals = other._terminals
94
105
  return self
95
106
 
107
+ if hasattr(other, "Ports"):
108
+ for final_output_node in self._terminals:
109
+ subgraph = final_output_node >> other
110
+ self._extend_edges(subgraph.edges)
111
+ self._terminals = {port for port in other.Ports}
112
+ return self
113
+
114
+ # other is a Port
96
115
  for final_output_node in self._terminals:
97
116
  subgraph = final_output_node >> other
98
117
  self._extend_edges(subgraph.edges)
99
- self._terminals = {port for port in other.Ports}
118
+ self._terminals = {other}
100
119
  return self
101
120
 
102
121
  @property
@@ -435,3 +435,28 @@ def test_graph__set_to_node():
435
435
 
436
436
  # AND two edges
437
437
  assert len(list(graph.edges)) == 2
438
+
439
+
440
+ def test_graph__node_to_port():
441
+ # GIVEN two nodes, one with a port
442
+ class SourceNode(BaseNode):
443
+ pass
444
+
445
+ class MiddleNode(BaseNode):
446
+ class Ports(BaseNode.Ports):
447
+ custom = Port.on_else()
448
+
449
+ class TargetNode(BaseNode):
450
+ pass
451
+
452
+ # WHEN we create a graph from the source node to the target node
453
+ graph = SourceNode >> MiddleNode.Ports.custom >> TargetNode
454
+
455
+ # THEN the graph has the source node as the entrypoint
456
+ assert set(graph.entrypoints) == {SourceNode}
457
+
458
+ # AND three nodes
459
+ assert len(list(graph.nodes)) == 3
460
+
461
+ # AND two edges
462
+ assert len(list(graph.edges)) == 2
@@ -25,7 +25,7 @@ def test_base_node__node_resolution__unset_pydantic_fields():
25
25
  assert node.data.dict() == my_data.dict()
26
26
 
27
27
 
28
- def test_base_node__node_resolution__descriptor_in_dict():
28
+ def test_base_node__node_resolution__descriptors_in_dict():
29
29
  # GIVEN an Input and State class
30
30
  class Inputs(BaseInputs):
31
31
  hello: str
@@ -3,14 +3,18 @@ from queue import Empty, Queue
3
3
  from threading import Thread
4
4
  from typing import TYPE_CHECKING, Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union, overload
5
5
 
6
+ from vellum.workflows.context import execution_context, get_parent_context
6
7
  from vellum.workflows.descriptors.base import BaseDescriptor
7
8
  from vellum.workflows.errors.types import VellumErrorCode
9
+ from vellum.workflows.events.types import ParentContext
8
10
  from vellum.workflows.exceptions import NodeException
9
11
  from vellum.workflows.inputs.base import BaseInputs
10
12
  from vellum.workflows.nodes.bases import BaseNode
11
13
  from vellum.workflows.outputs import BaseOutputs
12
14
  from vellum.workflows.state.base import BaseState
15
+ from vellum.workflows.state.context import WorkflowContext
13
16
  from vellum.workflows.types.generics import NodeType, StateType
17
+ from vellum.workflows.workflows.event_filters import all_workflow_event_filter
14
18
 
15
19
  if TYPE_CHECKING:
16
20
  from vellum.workflows import BaseWorkflow
@@ -53,7 +57,15 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
53
57
  fulfilled_iterations: List[bool] = []
54
58
  for index, item in enumerate(self.items):
55
59
  fulfilled_iterations.append(False)
56
- thread = Thread(target=self._run_subworkflow, kwargs={"item": item, "index": index})
60
+ parent_context = get_parent_context() or self._context.parent_context
61
+ thread = Thread(
62
+ target=self._context_run_subworkflow,
63
+ kwargs={
64
+ "item": item,
65
+ "index": index,
66
+ "parent_context": parent_context,
67
+ },
68
+ )
57
69
  thread.start()
58
70
 
59
71
  try:
@@ -62,6 +74,7 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
62
74
  while map_node_event := self._event_queue.get():
63
75
  index = map_node_event[0]
64
76
  terminal_event = map_node_event[1]
77
+ self._context._emit_subworkflow_event(terminal_event)
65
78
 
66
79
  if terminal_event.name == "workflow.execution.fulfilled":
67
80
  workflow_output_vars = vars(terminal_event.outputs)
@@ -85,12 +98,22 @@ class MapNode(BaseNode, Generic[StateType, MapNodeItemType]):
85
98
  )
86
99
  except Empty:
87
100
  pass
88
-
89
101
  return self.Outputs(**mapped_items)
90
102
 
103
+ def _context_run_subworkflow(
104
+ self, *, item: MapNodeItemType, index: int, parent_context: Optional[ParentContext] = None
105
+ ) -> None:
106
+ parent_context = parent_context or self._context.parent_context
107
+ with execution_context(parent_context=parent_context):
108
+ self._run_subworkflow(item=item, index=index)
109
+
91
110
  def _run_subworkflow(self, *, item: MapNodeItemType, index: int) -> None:
92
- subworkflow = self.subworkflow(parent_state=self.state, context=self._context)
93
- events = subworkflow.stream(inputs=self.SubworkflowInputs(index=index, item=item, all_items=self.items))
111
+ context = WorkflowContext(_vellum_client=self._context._vellum_client)
112
+ subworkflow = self.subworkflow(parent_state=self.state, context=context)
113
+ events = subworkflow.stream(
114
+ inputs=self.SubworkflowInputs(index=index, item=item, all_items=self.items),
115
+ event_filter=all_workflow_event_filter,
116
+ )
94
117
 
95
118
  for event in events:
96
119
  self._event_queue.put((index, event))
@@ -1,5 +1,6 @@
1
1
  import json
2
2
 
3
+ from vellum.workflows.nodes.bases.base import BaseNode
3
4
  from vellum.workflows.nodes.core.templating_node.node import TemplatingNode
4
5
 
5
6
 
@@ -19,3 +20,23 @@ def test_templating_node__dict_output():
19
20
 
20
21
  # THEN the output is json serializable
21
22
  assert json.loads(outputs.result) == {"key": "value"}
23
+
24
+
25
+ def test_templating_node__execution_count_reference():
26
+ # GIVEN a random node
27
+ class OtherNode(BaseNode):
28
+ pass
29
+
30
+ # AND a templating node that references the execution count of the random node
31
+ class TemplateNode(TemplatingNode):
32
+ template = "{{ total }}"
33
+ inputs = {
34
+ "total": OtherNode.Execution.count,
35
+ }
36
+
37
+ # WHEN the node is run
38
+ node = TemplateNode()
39
+ outputs = node.run()
40
+
41
+ # THEN the output is just the total
42
+ assert outputs.result == "0"
@@ -52,6 +52,9 @@ class Port:
52
52
  if isinstance(other, set) or isinstance(other, Graph):
53
53
  return Graph.from_port(self) >> other
54
54
 
55
+ if isinstance(other, Port):
56
+ return Graph.from_port(self) >> Graph.from_port(other)
57
+
55
58
  edge = Edge(from_port=self, to_node=other)
56
59
  if edge not in self._edges:
57
60
  self._edges.append(edge)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.11.1
3
+ Version: 0.11.4
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -2,22 +2,22 @@ vellum_cli/CONTRIBUTING.md,sha256=FtDC7BGxSeMnwCXAUssFsAIElXtmJE-O5Z7BpolcgvI,29
2
2
  vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
3
3
  vellum_cli/__init__.py,sha256=pftUQ6FiyfebNEB8xcfwzLjpfFDCAiH15xHBU6xr_wY,6733
4
4
  vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
5
- vellum_cli/config.py,sha256=uEdDqqpy7fh4Thlj2jSszfEmnXA9SGtzNzgR77fBjnY,3612
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
8
  vellum_cli/pull.py,sha256=6wIiorqSx2rmR6atZJHHBuLSviocxK_n0DQxEDGmCzo,4008
9
- vellum_cli/push.py,sha256=7arl5Udk9iTVkMIqbUWHE7HqzteTlRe0Ix3cc5RNaHA,5130
9
+ vellum_cli/push.py,sha256=kbvlzZ9KnkS5DxxKHQP5ZvHHk1-CbCDg9LqnIRAWyt4,5258
10
10
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- vellum_cli/tests/conftest.py,sha256=j3P2g5vegxarIi_VoWr1RlGxLYEQPgw3V_p3M6Iyq0U,1007
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=PzB259psqyllgxbN7VwH7-wvY0-Z3hvdsqGdCil9vDs,10286
15
- vellum_cli/tests/test_push.py,sha256=TBw35SXyFt9PL2OJV2jOPnbq3T0cbSVXGQ5TS77ytgA,5830
14
+ vellum_cli/tests/test_pull.py,sha256=N6ZphvHYGokclbpbTpgOmpu_m2GtocDEesbdeHFjO5Y,13194
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
18
18
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- vellum_ee/workflows/display/base.py,sha256=qwdT0mzR6SMSmF1hNaibvq_UDqrJPRQb1s9JHjYDlRY,1833
20
+ vellum_ee/workflows/display/base.py,sha256=3ZFUYRNKL24fBqXhKpa_Dq2W1a-a86J20dmJYA3H2eY,1755
21
21
  vellum_ee/workflows/display/nodes/__init__.py,sha256=5XOcZJXYUgaLS55QgRJzyQ_W1tpeprjnYAeYVezqoGw,160
22
22
  vellum_ee/workflows/display/nodes/base_node_display.py,sha256=3W7X1V2Lv0k6djYp60LDu-0lYVMNsEjPXmNmIQ4UW6s,5961
23
23
  vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=HoD3AGCMXKoHyyRJteUYlQ7DR26Srjhlrv4fZlLCyKc,1649
@@ -36,12 +36,12 @@ vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=3TJvHX_Uuf_gr9
36
36
  vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=sj-pySLVYGt032debhcQhHc5JwrALQrNCEKh3DXc8F8,7386
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
- vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=3fXhGIrWF7xr9lxH17SzzsDCkpXQ-99YmrAOzb-kuxw,2218
39
+ vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=BM3nfL0-D8x91xW0MGhnJFo45ZgGLXDqdbiSGoSuXN0,3244
40
40
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=9VpC3h0RYOxJuRbjDwidBYlLKakkmlEnDMBh2C7lHcY,1107
41
41
  vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=gLRkizwyw21-Z12IyDbdOJpXayiZZd4HWd6qgZQg8sc,3106
42
42
  vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=4KHb2icxH_vHF1GvGp_9ar_flSWthXrGZk6zMa17J_8,8430
43
43
  vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=zOp4voBSgB3MR1R93wTOrsiiara_hxEAYFupLl_SvTA,2657
44
- vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=qPu9TqXSeLYfFnydRp4T_eqHzQohv0Wxug-ixWTMLbQ,3019
44
+ vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=UNYxoE-89agE8ugK0aWg_uN61jPqlC2VSxWHk568sN4,3324
45
45
  vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=ZUp2fmDF4JTni1NjJOIV8dJoxx22eMBskmBJFsjtEvE,3809
47
47
  vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=m4d6hi6oD-jSW_bjrlN8coVwb6ivC2amPHpePHJ-Htg,2278
@@ -56,7 +56,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_s
56
56
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=vA8cd7PJYhf949OUGeYP_moKtMogSyfHN2Z-qzNQLwM,8294
57
57
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=N2ACycHn-EwP5paxHwDu2fufABssE293wiunhm-bCGg,22076
58
58
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=H1bVDG_mFPokJ7OYrnl9rM9M3gEa5bctGmhUuKccB4U,15950
59
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=fITHaT72ZmP8Y1AfYsJDdfRG6mz0EE9tusQYyZnheWw,9998
59
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=vm-dRH_Y-qU9whkwBUNnhVPYL_Ua6cqpaDsDSEgRkxo,9998
60
60
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py,sha256=NkpgaTbu6nLr3iwgsSNtiHyiNDCUaFakd1JaoW6CC6Y,9489
61
61
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=8bz0vm_EyQKSjnwS5vqqgnjE9ygvm-CaPKcwCfeOrlo,12704
62
62
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=xG8nKA1iKXxUe1fnD2X6qm7cUGW14iq2P-L16zhcKC8,4271
@@ -68,17 +68,17 @@ vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
68
68
  vellum_ee/workflows/display/utils/tests/test_uuids.py,sha256=ItjROhaPns8_mlvD17LIBwZKvhe2l0dXEd5oL-JiY64,448
69
69
  vellum_ee/workflows/display/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrgTY,739
70
70
  vellum_ee/workflows/display/utils/vellum.py,sha256=IlkKrfAAi4BV_HFnsdMFxNJC2-TrPM9ubVHv8ikmbSA,5110
71
- vellum_ee/workflows/display/vellum.py,sha256=62nPzLxn9cJdffwjL8vJG_1IDXXJrwaNdYeqsLNpbEQ,8800
71
+ vellum_ee/workflows/display/vellum.py,sha256=OSv0ZS50h1zJbunJ9TH7VEWFw-exXdK_ZsdzPxP9ROs,8814
72
72
  vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
73
73
  vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=HkakkrNgVFoHlUP7yHlQjHOvii3CZ90iyU1062PfoW4,12819
74
74
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=AMxNnTm2z3LIR5rqxoCAfuy37F2FTuSRDVtKUoezO8M,1184
75
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=fFs7NxrCnTRxHYOI7on9ClbRairQUc66BYRom3PeizA,17050
76
- vellum/__init__.py,sha256=tifpcqdWe2t7wU1HPqltMgM1AyY4Ruf7J06O1Dw2_Po,35436
75
+ vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=UiE1vJ4nzt1tHAxvXSt8qnV101I__gd5YdiPuKA4TWk,17370
76
+ vellum/__init__.py,sha256=QmGeEPXeFxgkZa849KKK3wH3Y641wyt00Rytfay6KiM,35520
77
77
  vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
78
78
  vellum/client/__init__.py,sha256=o4m7iRZWEV8rP3GkdaztHAjNmjxjWERlarviFoHzuKI,110927
79
79
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
80
80
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
81
- vellum/client/core/client_wrapper.py,sha256=GdzbbuiX9eXoSlOmj5ppAyjl_-ly0fPbAa8ecCrfSpE,1890
81
+ vellum/client/core/client_wrapper.py,sha256=2kb_aZbadYgFs7fHp9vZCvmnzLko_U_VPmjLg8Jnnyw,1890
82
82
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
83
83
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
84
84
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -137,7 +137,7 @@ vellum/client/resources/workflows/types/__init__.py,sha256=-uFca4ypncAOvfsg6sjD-
137
137
  vellum/client/resources/workflows/types/workflows_pull_request_format.py,sha256=dOWE_jnDnniIJLoeseeCms23aklghyBkoPmBFzcqqZk,165
138
138
  vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
139
139
  vellum/client/resources/workspace_secrets/client.py,sha256=h7UzXLyTttPq1t-JZGMg1BWxypxJvBGUdqg7KGT7MK4,8027
140
- vellum/client/types/__init__.py,sha256=ASrY-2_D-vIek3_WlkFzGDazEJH1UG5qRdd7jsNls8Q,53636
140
+ vellum/client/types/__init__.py,sha256=618t1NgjOnUJWFFv5pS4dmOIttrTkHhMNj5F8yRlTpA,53769
141
141
  vellum/client/types/ad_hoc_execute_prompt_event.py,sha256=bCjujA2XsOgyF3bRZbcEqV2rOIymRgsLoIRtZpB14xg,607
142
142
  vellum/client/types/ad_hoc_expand_meta.py,sha256=1gv-NCsy_6xBYupLvZH979yf2VMdxAU-l0y0ynMKZaw,1331
143
143
  vellum/client/types/ad_hoc_fulfilled_prompt_execution_meta.py,sha256=Bfvf1d_dkmshxRACVM5vcxbH_7AQY23RmrrnPc0ytYY,939
@@ -210,7 +210,7 @@ vellum/client/types/deployment_read.py,sha256=NtXmYsYJOATxkMxeVkSM35XzDVGbva3RWm
210
210
  vellum/client/types/deployment_release_tag_deployment_history_item.py,sha256=df4qKHT1f-z0jnRS4UmP8MQe6u3PwYej_d8KDF7EL88,631
211
211
  vellum/client/types/deployment_release_tag_read.py,sha256=YlwssIgBd5lKVqelH-gejQXQ7l31vrsRNMJKDGDyTEA,1129
212
212
  vellum/client/types/docker_service_token.py,sha256=T0icNHBKsIs6TrEiDRjckM_f37hcF1DMwEE8161tTvY,614
213
- vellum/client/types/document_document_to_document_index.py,sha256=LbXTZyYxA4hxewquf7ZLZgBD9uWGoIs1J64x4fY7bNg,1229
213
+ vellum/client/types/document_document_to_document_index.py,sha256=V0SkL-U3LHAL8dOp0fnOjdssuldkabZVJ2QCY_l5KFE,1404
214
214
  vellum/client/types/document_index_chunking.py,sha256=TU0Y7z0Xacm3dhzEDuDIG3ZKJCu3vNURRh3PqEd17mY,356
215
215
  vellum/client/types/document_index_chunking_request.py,sha256=g9BKCsHKg5kzjG7YYeMNQ_5R8TXLeSgumJlMXoSfBcs,435
216
216
  vellum/client/types/document_index_indexing_config.py,sha256=xL1pCzUOkw5sSie1OrBpasE3bVnv0UyZBn7uZztbhbs,781
@@ -455,7 +455,8 @@ vellum/client/types/sentence_chunker_config_request.py,sha256=EpGTP4z3YttiThYmdj
455
455
  vellum/client/types/sentence_chunking.py,sha256=guqU3072X4h8Laf6LhTWQ5lpjBpTgoXRxKp5iXJby2U,783
456
456
  vellum/client/types/sentence_chunking_request.py,sha256=77gv1fVc9IaTuGGx3O1HB0LF9sXM5pSTWksl8BEmvLU,812
457
457
  vellum/client/types/slim_deployment_read.py,sha256=DIYkuill3hfoNDYpgO8oZ7Aq9KGbM09dZv2gOEPPGeQ,1826
458
- vellum/client/types/slim_document.py,sha256=x3xcsgazw5wjlA-oMLqwb4b04QFvMvJGUnc9zrobfW0,2382
458
+ vellum/client/types/slim_document.py,sha256=frw1r47C11M-AsQ5UiDMW-EIuK0JTYUrSh-s6ZPtQSA,2395
459
+ vellum/client/types/slim_document_document_to_document_index.py,sha256=XDEkftIcN4w4LguyzSw7x-V-apbmxEAk2APZfAPzyiw,1346
459
460
  vellum/client/types/slim_workflow_deployment.py,sha256=FW7qXPtSIP9QAzECbl8gKL1iFqKWTAfIjSVwNTIL6GA,2189
460
461
  vellum/client/types/streaming_ad_hoc_execute_prompt_event.py,sha256=NdgmJ3AZMp6io-whZIGnGb49aiqz6__KafsrzjEF_9o,1183
461
462
  vellum/client/types/streaming_execute_prompt_event.py,sha256=bjfY5ZU8ZI048a7x1VW8dDXMtSl-3Ej5koSpfKboJj0,1161
@@ -1024,6 +1025,7 @@ vellum/types/sentence_chunking.py,sha256=MfuUfa_lhTX3OFCLJig0D7xN9lhLOxvsxw5ywHw
1024
1025
  vellum/types/sentence_chunking_request.py,sha256=f0am6XafUmK3Ok3dEy2cqeeqds63XF9qard0tcIzZJk,163
1025
1026
  vellum/types/slim_deployment_read.py,sha256=grU1w1TDVLwQqbklQaoShBRwIjeurD5ZadeK_ABP-VU,158
1026
1027
  vellum/types/slim_document.py,sha256=yhPuWFL65jMKdCEcMNBDqJZQc1UdYlH7tFJ1nkGwDL0,151
1028
+ vellum/types/slim_document_document_to_document_index.py,sha256=3FwldvGhBc0pkccorIE3eMi8hQ0swgCWgKQJ6-jIff0,178
1027
1029
  vellum/types/slim_workflow_deployment.py,sha256=wRfaObu07ouUTNln4QZuBiye2uuKh3kPPvz2h9sbmJU,162
1028
1030
  vellum/types/streaming_ad_hoc_execute_prompt_event.py,sha256=gKFR8SKLo837TRORLQtuQmikKGNrOSi7HeoSTsnLh3Y,175
1029
1031
  vellum/types/streaming_execute_prompt_event.py,sha256=9H0sLQjyLOH6lB1ouk4GFRmp4VkYjNQXh8p8auLzft8,168
@@ -1203,7 +1205,7 @@ vellum/workflows/__init__.py,sha256=CssPsbNvN6rDhoLuqpEv7MMKGa51vE6dvAh6U31Pcio,
1203
1205
  vellum/workflows/constants.py,sha256=Z0W4YlqfSlSgWC11PrVUPs6ZOBeIaQ78E_90J1hohiw,789
1204
1206
  vellum/workflows/context.py,sha256=R8qdsFbD_0p7B6PWnyvSrZ_aOgMtGw-_uk0P0UAmwLA,1230
1205
1207
  vellum/workflows/descriptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1206
- vellum/workflows/descriptors/base.py,sha256=VyyXtGJ_Hc34AOC8XC_Rw_68L4WMgD5w9Y7r8t8My4E,13814
1208
+ vellum/workflows/descriptors/base.py,sha256=2NQxURc_9QQ4pI9zICTZeULnS4OIml8fYcAkMgNU01Y,13830
1207
1209
  vellum/workflows/descriptors/tests/test_utils.py,sha256=icwW-YkHD5oR6rn9IH6Rck9yYOsuwnocyJVHoeJFd74,2849
1208
1210
  vellum/workflows/descriptors/utils.py,sha256=lO_dbr5g3PXpHPtVBkdguAK4-1qayZ7RXjl3BgAhrMM,3795
1209
1211
  vellum/workflows/edges/__init__.py,sha256=wSkmAnz9xyi4vZwtDbKxwlplt2skD7n3NsxkvR_pUus,50
@@ -1249,9 +1251,9 @@ vellum/workflows/expressions/not_between.py,sha256=H2huRR95D9Qb7lCHmK7BcK-Ug-E1g
1249
1251
  vellum/workflows/expressions/not_in.py,sha256=OQkN5G1E6VoTDpoLvx7X3GbohLlqEAYHV0rVVUV7ow4,1049
1250
1252
  vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
1251
1253
  vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
1252
- vellum/workflows/graph/graph.py,sha256=etuop6xNRJcK8anQx-I31xaExw604f9wC-lRQFr2Xw8,4571
1254
+ vellum/workflows/graph/graph.py,sha256=rmPPs2gtQhaYIuZKETdWrdacgwQRvT6soj12UOl1Tm0,5316
1253
1255
  vellum/workflows/graph/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1254
- vellum/workflows/graph/tests/test_graph.py,sha256=zeBqfcMIlMS3AJn-t0YitKsa8O0r2fDnQ7qBPxexP7k,10620
1256
+ vellum/workflows/graph/tests/test_graph.py,sha256=PQI1lO2IY-izBSbkwxjKR5a1z-aN-ieHV_p0m-g5eAM,11256
1255
1257
  vellum/workflows/inputs/__init__.py,sha256=AbFEteIYEvCb14fM3EK7bhM-40-6s494rSlIhQ4Dsss,62
1256
1258
  vellum/workflows/inputs/base.py,sha256=1kMgr0WqCYdWUqgFvgSoAMw2067FAlgwhGXLgbIOrLY,2391
1257
1259
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
@@ -1261,14 +1263,14 @@ vellum/workflows/nodes/bases/base.py,sha256=fKEycP9yqcHjH781d4Sg5NFHZFDsbco0nunr
1261
1263
  vellum/workflows/nodes/bases/base_subworkflow_node/__init__.py,sha256=0nkHQiFC4IpA1ZGx60XG0BLUWF6hwUpgqmS3ZrlFGhg,80
1262
1264
  vellum/workflows/nodes/bases/base_subworkflow_node/node.py,sha256=vC0gUBQewAUNtP3i2G0-LUpE_kY-r_ijBD_tS1XkQ1E,383
1263
1265
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1264
- vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=7M-hmMTClhmD3OSDeZgXxAGJqfi6kM9uf5Mp4L4VooI,3111
1266
+ vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=A4hpFaG4TwpumTi4FV4KDMyUeixBc26mRzSIihxFiGo,3112
1265
1267
  vellum/workflows/nodes/core/__init__.py,sha256=5zDMCmyt1v0HTJzlUBwq3U9L825yZGZhT9JL18-mRR4,455
1266
1268
  vellum/workflows/nodes/core/error_node/__init__.py,sha256=g7RRnlHhqu4qByfLjBwCunmgGA8dI5gNsjS3h6TwlSI,60
1267
1269
  vellum/workflows/nodes/core/error_node/node.py,sha256=hqBPHoLnhNrK9ITIaEzpnk47XYDbG6cmObz7oe78Ceg,944
1268
1270
  vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py,sha256=nKNEH1QTl-1PcvmYoqSWEl0-t6gAur8GLTXHzklRQfM,84
1269
1271
  vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=kkPb2opZ7J6pIWueU-WU43KXEwKY5GrANldmesRFetI,3469
1270
1272
  vellum/workflows/nodes/core/map_node/__init__.py,sha256=MXpZYmGfhsMJHqqlpd64WiJRtbAtAMQz-_3fCU_cLV0,56
1271
- vellum/workflows/nodes/core/map_node/node.py,sha256=aPhV3niv_jWSwrZ2CwiRg0CDOM-09Fa6QqOPYNJMgRc,6206
1273
+ vellum/workflows/nodes/core/map_node/node.py,sha256=LEohCiS1HffS5dD6V0GWgjktf3Z4J9v7FZdVDNTpF5o,7284
1272
1274
  vellum/workflows/nodes/core/map_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1273
1275
  vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=RHSZs7t6mW3UWvRrXnHZqaXVdRT2ZquOK_YHJ-gzXsU,1871
1274
1276
  vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
@@ -1280,7 +1282,7 @@ vellum/workflows/nodes/core/templating_node/custom_filters.py,sha256=Q0DahYRHP4K
1280
1282
  vellum/workflows/nodes/core/templating_node/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
1281
1283
  vellum/workflows/nodes/core/templating_node/node.py,sha256=19OFvRimrp1YuUO7H4rU7ZDsuawKvuwwVEAix7qBugs,4500
1282
1284
  vellum/workflows/nodes/core/templating_node/render.py,sha256=OpJp0NAH6qcEL6K9lxR0qjpFb75TYNttJR5iCos8tmg,1792
1283
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=L6F3Gw9doguj1TSKmAns-mzXvoRuRivaCFe3mhjo13E,551
1285
+ vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=0BtXeSix7KGIuKzlPFTMLATpNnFPhut1UV_srGptkt0,1120
1284
1286
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1285
1287
  vellum/workflows/nodes/core/try_node/node.py,sha256=5LHTPFsJHZ0eZZbXdmNswj1sLs93W8Pa1dp6JSZBnPM,6538
1286
1288
  vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1332,7 +1334,7 @@ vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfv
1332
1334
  vellum/workflows/outputs/base.py,sha256=a7W6rNSDSawwGAXYjNTF2iHb9lnZu7WFSOagZIyy__k,7976
1333
1335
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
1334
1336
  vellum/workflows/ports/node_ports.py,sha256=g4A-8iUAvEJSkaWppbvzAR8XU02R9U-qLN4rP2Kq4Aw,2743
1335
- vellum/workflows/ports/port.py,sha256=iLYzjIoFk5HWpNyhySf8E1uIuJ3MbAeJGD3yOnnLY70,2843
1337
+ vellum/workflows/ports/port.py,sha256=4vTvhe9FE5SO5WwwAmJm2ApBBBxZv9fsyUoikPIgiaI,2947
1336
1338
  vellum/workflows/ports/utils.py,sha256=pEjVNJKw9LhD_cFN-o0MWBOW2ejno7jv26qqzjLxwS4,1662
1337
1339
  vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcvEcsUBrzLIs,591
1338
1340
  vellum/workflows/references/environment_variable.py,sha256=7FFtiKfc4eyVkkfUbhc666OBNDqvFlMoNQEYmGpEVVE,661
@@ -1375,8 +1377,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
1375
1377
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1376
1378
  vellum/workflows/workflows/base.py,sha256=mnI-kZ78yt7u6NFSTUo-tYjDnarP-RJ7uZjwjCn6PCQ,16795
1377
1379
  vellum/workflows/workflows/event_filters.py,sha256=-uQcMB7IpPd-idMku8f2QNVhPXPFWo6FZLlGjRf8rCo,1996
1378
- vellum_ai-0.11.1.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1379
- vellum_ai-0.11.1.dist-info/METADATA,sha256=OKYg7BRTEYyEI-8T2G9Clrzq8lFPG6vzw4zgEvr93vk,5128
1380
- vellum_ai-0.11.1.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
1381
- vellum_ai-0.11.1.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1382
- vellum_ai-0.11.1.dist-info/RECORD,,
1380
+ vellum_ai-0.11.4.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1381
+ vellum_ai-0.11.4.dist-info/METADATA,sha256=jsthltxAbW8_ohEuJ38axnhG6JBG-DLG6p2bwAgcAwA,5128
1382
+ vellum_ai-0.11.4.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
1383
+ vellum_ai-0.11.4.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1384
+ vellum_ai-0.11.4.dist-info/RECORD,,
vellum_cli/config.py CHANGED
@@ -20,6 +20,15 @@ class WorkflowDeploymentConfig(UniversalBaseModel):
20
20
  description: Optional[str] = None
21
21
  release_tags: Optional[List[str]] = None
22
22
 
23
+ def merge(self, other: "WorkflowDeploymentConfig") -> "WorkflowDeploymentConfig":
24
+ return WorkflowDeploymentConfig(
25
+ id=self.id or other.id,
26
+ label=self.label or other.label,
27
+ name=self.name or other.name,
28
+ description=self.description or other.description,
29
+ release_tags=self.release_tags or other.release_tags,
30
+ )
31
+
23
32
 
24
33
  class WorkflowConfig(UniversalBaseModel):
25
34
  module: str
@@ -28,10 +37,37 @@ class WorkflowConfig(UniversalBaseModel):
28
37
  deployments: List[WorkflowDeploymentConfig] = field(default_factory=list)
29
38
 
30
39
  def merge(self, other: "WorkflowConfig") -> "WorkflowConfig":
40
+ self_deployment_by_id = {
41
+ deployment.id: deployment for deployment in self.deployments if deployment.id is not None
42
+ }
43
+ other_deployment_by_id = {
44
+ deployment.id: deployment for deployment in other.deployments if deployment.id is not None
45
+ }
46
+ all_ids = sorted(set(self_deployment_by_id.keys()).union(set(other_deployment_by_id.keys())))
47
+ merged_deployments = []
48
+ for id in all_ids:
49
+ self_deployment = self_deployment_by_id.get(id)
50
+ other_deployment = other_deployment_by_id.get(id)
51
+ if self_deployment and other_deployment:
52
+ merged_deployments.append(self_deployment.merge(other_deployment))
53
+ elif self_deployment:
54
+ merged_deployments.append(self_deployment)
55
+ elif other_deployment:
56
+ merged_deployments.append(other_deployment)
57
+
58
+ for deployment in self.deployments:
59
+ if deployment.id is None:
60
+ merged_deployments.append(deployment)
61
+
62
+ for deployment in other.deployments:
63
+ if deployment.id is None:
64
+ merged_deployments.append(deployment)
65
+
31
66
  return WorkflowConfig(
32
67
  module=self.module,
33
68
  workflow_sandbox_id=self.workflow_sandbox_id or other.workflow_sandbox_id,
34
69
  ignore=self.ignore or other.ignore,
70
+ deployments=merged_deployments,
35
71
  )
36
72
 
37
73
 
vellum_cli/push.py CHANGED
@@ -1,3 +1,4 @@
1
+ from importlib import metadata
1
2
  import io
2
3
  import json
3
4
  import os
@@ -50,6 +51,9 @@ def push_command(
50
51
  workflow = BaseWorkflow.load_from_module(workflow_config.module)
51
52
  workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=workflow)
52
53
  exec_config = workflow_display.serialize()
54
+ exec_config["runner_config"] = {
55
+ "sdk_version": metadata.version("vellum-ai"),
56
+ }
53
57
 
54
58
  label = snake_to_title_case(workflow_config.module.split(".")[-1])
55
59
 
@@ -1,19 +1,29 @@
1
1
  import pytest
2
+ from dataclasses import dataclass
2
3
  import os
3
4
  import shutil
4
5
  import tempfile
5
6
  from uuid import uuid4
6
- from typing import Any, Callable, Dict, Generator, Tuple
7
+ from typing import Any, Callable, Dict, Generator
7
8
 
8
9
  import tomli_w
9
10
 
10
11
 
12
+ @dataclass
13
+ class MockModuleResult:
14
+ temp_dir: str
15
+ module: str
16
+ set_pyproject_toml: Callable[[Dict[str, Any]], None]
17
+ workflow_sandbox_id: str
18
+
19
+
11
20
  @pytest.fixture
12
- def mock_module() -> Generator[Tuple[str, str, Callable[[Dict[str, Any]], None]], None, None]:
21
+ def mock_module() -> Generator[MockModuleResult, None, None]:
13
22
  current_dir = os.getcwd()
14
23
  temp_dir = tempfile.mkdtemp()
15
24
  os.chdir(temp_dir)
16
25
  module = "examples.mock"
26
+ workflow_sandbox_id = str(uuid4())
17
27
 
18
28
  def set_pyproject_toml(vellum_config: Dict[str, Any]) -> None:
19
29
  pyproject_toml_path = os.path.join(temp_dir, "pyproject.toml")
@@ -28,13 +38,18 @@ def mock_module() -> Generator[Tuple[str, str, Callable[[Dict[str, Any]], None]]
28
38
  "workflows": [
29
39
  {
30
40
  "module": module,
31
- "workflow_sandbox_id": str(uuid4()),
41
+ "workflow_sandbox_id": workflow_sandbox_id,
32
42
  }
33
43
  ]
34
44
  }
35
45
  )
36
46
 
37
- yield temp_dir, module, set_pyproject_toml
47
+ yield MockModuleResult(
48
+ temp_dir=temp_dir,
49
+ module=module,
50
+ set_pyproject_toml=set_pyproject_toml,
51
+ workflow_sandbox_id=workflow_sandbox_id,
52
+ )
38
53
 
39
54
  os.chdir(current_dir)
40
55
  shutil.rmtree(temp_dir)
@@ -37,7 +37,8 @@ def _zip_file_map(file_map: dict[str, str]) -> bytes:
37
37
  )
38
38
  def test_pull(vellum_client, mock_module, base_command):
39
39
  # GIVEN a module on the user's filesystem
40
- temp_dir, module, _ = mock_module
40
+ temp_dir = mock_module.temp_dir
41
+ module = mock_module.module
41
42
 
42
43
  # AND the workflow pull API call returns a zip file
43
44
  vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
@@ -58,7 +59,9 @@ def test_pull(vellum_client, mock_module, base_command):
58
59
 
59
60
  def test_pull__second_module(vellum_client, mock_module):
60
61
  # GIVEN a module on the user's filesystem
61
- temp_dir, module, set_pyproject_toml = mock_module
62
+ temp_dir = mock_module.temp_dir
63
+ module = mock_module.module
64
+ set_pyproject_toml = mock_module.set_pyproject_toml
62
65
 
63
66
  # AND the workflow pull API call returns a zip file
64
67
  vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
@@ -134,7 +137,7 @@ def test_pull__sandbox_id_with_no_config(vellum_client):
134
137
 
135
138
  def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_module):
136
139
  # GIVEN a pyproject.toml with a workflow configured
137
- temp_dir, _, _ = mock_module
140
+ temp_dir = mock_module.temp_dir
138
141
 
139
142
  # AND a different workflow sandbox id
140
143
  workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
@@ -163,7 +166,8 @@ def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_mod
163
166
 
164
167
  def test_pull__remove_missing_files(vellum_client, mock_module):
165
168
  # GIVEN a module on the user's filesystem
166
- temp_dir, module, _ = mock_module
169
+ temp_dir = mock_module.temp_dir
170
+ module = mock_module.module
167
171
 
168
172
  # AND the workflow pull API call returns a zip file
169
173
  vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
@@ -192,7 +196,9 @@ def test_pull__remove_missing_files(vellum_client, mock_module):
192
196
 
193
197
  def test_pull__remove_missing_files__ignore_pattern(vellum_client, mock_module):
194
198
  # GIVEN a module on the user's filesystem
195
- temp_dir, module, set_pyproject_toml = mock_module
199
+ temp_dir = mock_module.temp_dir
200
+ module = mock_module.module
201
+ set_pyproject_toml = mock_module.set_pyproject_toml
196
202
 
197
203
  # AND the workflow pull API call returns a zip file
198
204
  vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
@@ -243,7 +249,7 @@ def test_pull__remove_missing_files__ignore_pattern(vellum_client, mock_module):
243
249
 
244
250
  def test_pull__include_json(vellum_client, mock_module):
245
251
  # GIVEN a module on the user's filesystem
246
- _, module, __ = mock_module
252
+ module = mock_module.module
247
253
 
248
254
  # AND the workflow pull API call returns a zip file
249
255
  vellum_client.workflows.pull.return_value = iter(
@@ -265,7 +271,7 @@ def test_pull__include_json(vellum_client, mock_module):
265
271
 
266
272
  def test_pull__exclude_code(vellum_client, mock_module):
267
273
  # GIVEN a module on the user's filesystem
268
- _, module, __ = mock_module
274
+ module = mock_module.module
269
275
 
270
276
  # AND the workflow pull API call returns a zip file
271
277
  vellum_client.workflows.pull.return_value = iter(
@@ -283,3 +289,75 @@ def test_pull__exclude_code(vellum_client, mock_module):
283
289
  vellum_client.workflows.pull.assert_called_once()
284
290
  call_args = vellum_client.workflows.pull.call_args.kwargs
285
291
  assert call_args["request_options"]["additional_query_parameters"] == {"exclude_code": True}
292
+
293
+
294
+ def test_pull__sandbox_id_with_other_workflow_deployment_in_lock(vellum_client, mock_module):
295
+ # GIVEN a pyproject.toml with a workflow configured
296
+ temp_dir = mock_module.temp_dir
297
+ module = mock_module.module
298
+ workflow_sandbox_id = mock_module.workflow_sandbox_id
299
+
300
+ # AND there's a workflow deployment in the lock file
301
+ vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
302
+ with open(vellum_lock_json, "w") as f:
303
+ json.dump(
304
+ {
305
+ "version": "1.0",
306
+ "workflows": [
307
+ {
308
+ "module": module,
309
+ "workflow_sandbox_id": "0edc07cd-45b9-43e8-99bc-1f181972a857",
310
+ "ignore": "tests/*",
311
+ "deployments": [
312
+ {
313
+ "id": "7e5a7610-4c46-4bc9-b06e-0fc6a9e28959",
314
+ "label": None,
315
+ "name": None,
316
+ "description": None,
317
+ "release_tags": None,
318
+ }
319
+ ],
320
+ }
321
+ ],
322
+ },
323
+ f,
324
+ )
325
+
326
+ # AND a different workflow sandbox id
327
+ new_workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
328
+
329
+ # AND the workflow pull API call returns a zip file
330
+ vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
331
+
332
+ # WHEN the user runs the pull command with the new workflow sandbox id
333
+ runner = CliRunner()
334
+ result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-sandbox-id", new_workflow_sandbox_id])
335
+
336
+ # THEN the command returns successfully
337
+ assert result.exit_code == 0
338
+
339
+ # AND the lock file is updated to preserve the deployment and include the new workflow
340
+ with open(vellum_lock_json) as f:
341
+ lock_data = json.load(f)
342
+ assert lock_data["workflows"] == [
343
+ {
344
+ "module": module,
345
+ "workflow_sandbox_id": workflow_sandbox_id,
346
+ "ignore": "tests/*",
347
+ "deployments": [
348
+ {
349
+ "id": "7e5a7610-4c46-4bc9-b06e-0fc6a9e28959",
350
+ "label": None,
351
+ "name": None,
352
+ "description": None,
353
+ "release_tags": None,
354
+ },
355
+ ],
356
+ },
357
+ {
358
+ "module": "workflow_87654321",
359
+ "workflow_sandbox_id": new_workflow_sandbox_id,
360
+ "ignore": None,
361
+ "deployments": [],
362
+ },
363
+ ]
@@ -29,8 +29,7 @@ def _extract_tar_gz(tar_gz_bytes: bytes) -> dict[str, str]:
29
29
 
30
30
  def test_push__no_config(mock_module):
31
31
  # GIVEN no config file set
32
- _, _, set_pyproject_toml = mock_module
33
- set_pyproject_toml({"workflows": []})
32
+ mock_module.set_pyproject_toml({"workflows": []})
34
33
 
35
34
  # WHEN calling `vellum push`
36
35
  runner = CliRunner()
@@ -44,8 +43,7 @@ def test_push__no_config(mock_module):
44
43
 
45
44
  def test_push__multiple_workflows_configured__no_module_specified(mock_module):
46
45
  # GIVEN multiple workflows configured
47
- _, _, set_pyproject_toml = mock_module
48
- set_pyproject_toml({"workflows": [{"module": "examples.mock"}, {"module": "examples.mock2"}]})
46
+ mock_module.set_pyproject_toml({"workflows": [{"module": "examples.mock"}, {"module": "examples.mock2"}]})
49
47
 
50
48
  # WHEN calling `vellum push` without a module specified
51
49
  runner = CliRunner()
@@ -62,8 +60,8 @@ def test_push__multiple_workflows_configured__no_module_specified(mock_module):
62
60
 
63
61
  def test_push__multiple_workflows_configured__not_found_module(mock_module):
64
62
  # GIVEN multiple workflows configured
65
- _, module, set_pyproject_toml = mock_module
66
- set_pyproject_toml({"workflows": [{"module": "examples.mock2"}, {"module": "examples.mock3"}]})
63
+ module = mock_module.module
64
+ mock_module.set_pyproject_toml({"workflows": [{"module": "examples.mock2"}, {"module": "examples.mock3"}]})
67
65
 
68
66
  # WHEN calling `vellum push` with a module that doesn't exist
69
67
  runner = CliRunner()
@@ -85,7 +83,8 @@ def test_push__multiple_workflows_configured__not_found_module(mock_module):
85
83
  )
86
84
  def test_push__happy_path(mock_module, vellum_client, base_command):
87
85
  # GIVEN a single workflow configured
88
- temp_dir, module, _ = mock_module
86
+ temp_dir = mock_module.temp_dir
87
+ module = mock_module.module
89
88
 
90
89
  # AND a workflow exists in the module successfully
91
90
  base_dir = os.path.join(temp_dir, *module.split("."))
@@ -134,7 +133,8 @@ class ExampleWorkflow(BaseWorkflow):
134
133
  )
135
134
  def test_push__deployment(mock_module, vellum_client, base_command):
136
135
  # GIVEN a single workflow configured
137
- temp_dir, module, _ = mock_module
136
+ temp_dir = mock_module.temp_dir
137
+ module = mock_module.module
138
138
 
139
139
  # AND a workflow exists in the module successfully
140
140
  base_dir = os.path.join(temp_dir, *module.split("."))
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from uuid import UUID
3
- from typing import Optional, TypeVar
3
+ from typing import TypeVar
4
4
 
5
5
 
6
6
  @dataclass
@@ -20,8 +20,6 @@ WorkflowMetaDisplayOverridesType = TypeVar("WorkflowMetaDisplayOverridesType", b
20
20
  @dataclass
21
21
  class WorkflowInputsDisplayOverrides:
22
22
  id: UUID
23
- required: Optional[bool] = None
24
- color: Optional[str] = None
25
23
 
26
24
 
27
25
  @dataclass
@@ -1,5 +1,5 @@
1
1
  from uuid import UUID
2
- from typing import Any, ClassVar, Generic, List, Optional, TypeVar
2
+ from typing import Any, ClassVar, Generic, List, Optional, Type, TypeVar
3
3
 
4
4
  from vellum.workflows.nodes.displayable import MergeNode
5
5
  from vellum.workflows.types.core import JsonObject
@@ -14,6 +14,10 @@ _MergeNodeType = TypeVar("_MergeNodeType", bound=MergeNode)
14
14
  class BaseMergeNodeDisplay(BaseNodeVellumDisplay[_MergeNodeType], Generic[_MergeNodeType]):
15
15
  target_handle_ids: ClassVar[List[UUID]]
16
16
 
17
+ def __init__(self, node: Type[_MergeNodeType]):
18
+ super().__init__(node)
19
+ self._target_handle_iterator = 0
20
+
17
21
  def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
18
22
  node = self._node
19
23
  node_id = self.node_id
@@ -46,3 +50,18 @@ class BaseMergeNodeDisplay(BaseNodeVellumDisplay[_MergeNodeType], Generic[_Merge
46
50
 
47
51
  def get_target_handle_ids(self) -> Optional[List[UUID]]:
48
52
  return self._get_explicit_node_display_attr("target_handle_ids", List[UUID])
53
+
54
+ def get_target_handle_id_by_source_node_id(self, source_node_id: UUID) -> UUID:
55
+ target_handle_ids = self.get_target_handle_ids()
56
+ if target_handle_ids is None:
57
+ return uuid4_from_hash(f"{self.node_id}|target_handle|{source_node_id}")
58
+
59
+ # Edges call this method to know which handle to connect to
60
+ # We use the order of the edges to determine the handle id. This is quite brittle to the order of the
61
+ # edges matching the order of the Merge Node's data.target_handles attribute. We should look into a
62
+ # longer term solution, or cutover Merge Nodes to Generic Nodes soon
63
+ target_handle_id = target_handle_ids[self._target_handle_iterator]
64
+ self._target_handle_iterator += 1
65
+ if self._target_handle_iterator >= len(self.target_handle_ids):
66
+ self._target_handle_iterator = 0
67
+ return target_handle_id
@@ -1,5 +1,5 @@
1
1
  from uuid import UUID
2
- from typing import ClassVar, Dict, Generic, Optional, TypeVar
2
+ from typing import ClassVar, Generic, Optional, TypeVar
3
3
 
4
4
  from vellum.workflows.nodes.core.templating_node import TemplatingNode
5
5
  from vellum.workflows.types.core import JsonObject
@@ -11,10 +11,11 @@ from vellum_ee.workflows.display.types import WorkflowDisplayContext
11
11
 
12
12
  _TemplatingNodeType = TypeVar("_TemplatingNodeType", bound=TemplatingNode)
13
13
 
14
+ TEMPLATE_INPUT_NAME = "template"
15
+
14
16
 
15
17
  class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Generic[_TemplatingNodeType]):
16
18
  template_input_id: ClassVar[Optional[UUID]] = None
17
- input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
18
19
 
19
20
  def serialize(
20
21
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
@@ -22,12 +23,21 @@ class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Gene
22
23
  node = self._node
23
24
  node_id = self.node_id
24
25
 
26
+ template_input_id = self.template_input_id or next(
27
+ (
28
+ input_id
29
+ for input_name, input_id in self.node_input_ids_by_name.items()
30
+ if input_name == TEMPLATE_INPUT_NAME
31
+ ),
32
+ None,
33
+ )
34
+
25
35
  template_node_input = create_node_input(
26
36
  node_id=node_id,
27
37
  input_name="template",
28
38
  value=node.template,
29
39
  display_context=display_context,
30
- input_id=self.template_input_id,
40
+ input_id=template_input_id,
31
41
  )
32
42
  template_node_inputs = raise_if_descriptor(node.inputs)
33
43
  template_inputs = [
@@ -36,9 +46,10 @@ class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Gene
36
46
  input_name=variable_name,
37
47
  value=variable_value,
38
48
  display_context=display_context,
39
- input_id=self.input_ids_by_name.get("template"),
49
+ input_id=self.node_input_ids_by_name.get(variable_name),
40
50
  )
41
51
  for variable_name, variable_value in template_node_inputs.items()
52
+ if variable_name != TEMPLATE_INPUT_NAME
42
53
  ]
43
54
  node_inputs = [template_node_input, *template_inputs]
44
55
 
@@ -218,7 +218,7 @@ def test_serialize_workflow__await_all():
218
218
  "source_node_id": "59243c65-053f-4ea6-9157-3f3edb1477bf",
219
219
  "source_handle_id": "b9c5f52b-b714-46e8-a09c-38b4e770dd36",
220
220
  "target_node_id": "37c10e8a-771b-432b-a767-31f5007851f0",
221
- "target_handle_id": "0efd256f-f5f6-45fe-9adb-651780f5e63d",
221
+ "target_handle_id": "42eeb66c-9792-4609-8c71-3a56f668f4dc",
222
222
  "type": "DEFAULT",
223
223
  },
224
224
  {
@@ -226,7 +226,7 @@ def test_serialize_workflow__await_all():
226
226
  "source_node_id": "127ef456-91bc-43c6-bd8b-1772db5e3cb5",
227
227
  "source_handle_id": "b0bd17f3-4ce6-4232-9666-ec8afa161bf2",
228
228
  "target_node_id": "37c10e8a-771b-432b-a767-31f5007851f0",
229
- "target_handle_id": "0efd256f-f5f6-45fe-9adb-651780f5e63d",
229
+ "target_handle_id": "f40ff7fb-de1b-4aa4-ba3c-7630f7357cbf",
230
230
  "type": "DEFAULT",
231
231
  },
232
232
  {
@@ -74,7 +74,9 @@ class WorkflowMetaVellumDisplay(WorkflowMetaVellumDisplayOverrides):
74
74
 
75
75
  @dataclass
76
76
  class WorkflowInputsVellumDisplayOverrides(WorkflowInputsDisplay, WorkflowInputsDisplayOverrides):
77
- pass
77
+ name: Optional[str] = None
78
+ required: Optional[bool] = None
79
+ color: Optional[str] = None
78
80
 
79
81
 
80
82
  @dataclass
@@ -115,8 +117,6 @@ class WorkflowOutputVellumDisplayOverrides(WorkflowOutputDisplay, WorkflowOutput
115
117
  target_handle_id: UUID
116
118
  display_data: NodeDisplayData
117
119
  edge_id: UUID
118
- # source_node_id: Optional[UUID]
119
- # source_handle_id: Optional[UUID]
120
120
 
121
121
 
122
122
  @dataclass
@@ -12,6 +12,7 @@ from vellum.workflows.references import WorkflowInputReference
12
12
  from vellum.workflows.references.output import OutputReference
13
13
  from vellum.workflows.types.core import JsonArray, JsonObject
14
14
  from vellum.workflows.types.generics import WorkflowType
15
+ from vellum_ee.workflows.display.nodes import BaseMergeNodeDisplay
15
16
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
16
17
  from vellum_ee.workflows.display.nodes.types import PortDisplay
17
18
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
@@ -66,7 +67,7 @@ class VellumWorkflowDisplay(
66
67
  input_variables.append(
67
68
  {
68
69
  "id": str(workflow_input_display.id),
69
- "key": workflow_input.name,
70
+ "key": workflow_input_display.name or workflow_input.name,
70
71
  "type": infer_vellum_variable_type(workflow_input),
71
72
  "default": default.dict() if default else None,
72
73
  "required": required,
@@ -366,7 +367,12 @@ class VellumWorkflowDisplay(
366
367
 
367
368
  target_node_display = node_displays[target_node]
368
369
  target_node_id = target_node_display.node_id
369
- target_handle_id = target_node_display.get_target_handle_id()
370
+
371
+ target_handle_id: UUID
372
+ if isinstance(target_node_display, BaseMergeNodeDisplay):
373
+ target_handle_id = target_node_display.get_target_handle_id_by_source_node_id(source_node_id)
374
+ else:
375
+ target_handle_id = target_node_display.get_target_handle_id()
370
376
 
371
377
  return self._generate_edge_display_from_source(
372
378
  source_node_id, source_handle_id, target_node_id, target_handle_id, overrides