vellum-ai 0.11.1__py3-none-any.whl → 0.11.4__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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