vellum-ai 0.11.3__py3-none-any.whl → 0.11.5__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.
@@ -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.2",
20
+ "X-Fern-SDK-Version": "0.11.5",
21
21
  }
22
22
  headers["X_API_KEY"] = self.api_key
23
23
  return headers
@@ -87,6 +87,13 @@ class WorkflowParentContext(BaseParentContext):
87
87
  workflow_definition: VellumCodeResourceDefinition
88
88
 
89
89
 
90
+ class WorkflowSandboxParentContext(BaseParentContext):
91
+ type: Literal["WORKFLOW_SANDBOX"] = "WORKFLOW_SANDBOX"
92
+ sandbox_id: UUID
93
+ sandbox_history_item_id: UUID
94
+ scenario_id: UUID
95
+
96
+
90
97
  # Define the discriminated union
91
98
  ParentContext = Annotated[
92
99
  Union[
@@ -94,6 +101,7 @@ ParentContext = Annotated[
94
101
  NodeParentContext,
95
102
  WorkflowDeploymentParentContext,
96
103
  PromptDeploymentParentContext,
104
+ WorkflowSandboxParentContext,
97
105
  ],
98
106
  Field(discriminator="type"),
99
107
  ]
@@ -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
@@ -57,7 +57,7 @@ class BaseNodeMeta(type):
57
57
  dct["Ports"] = type(
58
58
  f"{name}.Ports",
59
59
  (NodePorts,),
60
- dict(dct["Ports"].__dict__),
60
+ {**dct["Ports"].__dict__, "__module__": dct["__module__"]},
61
61
  )
62
62
  else:
63
63
  for base in reversed(bases):
@@ -160,6 +160,7 @@ Message: {event.error.message}""",
160
160
  "__module__": dynamic_module,
161
161
  "on_error_code": _on_error_code,
162
162
  "subworkflow": Subworkflow,
163
+ "Ports": type("Ports", (TryNode.Ports,), {port.name: port.copy() for port in inner_cls.Ports}),
163
164
  },
164
165
  )
165
166
  return WrappedNode
@@ -40,6 +40,14 @@ class Port:
40
40
  def __repr__(self) -> str:
41
41
  return f"{self.node_class}.Ports.{self.name}"
42
42
 
43
+ def copy(self) -> "Port":
44
+ return Port(
45
+ default=self.default,
46
+ fork_state=self._fork_state,
47
+ condition=self._condition,
48
+ condition_type=self._condition_type,
49
+ )
50
+
43
51
  @property
44
52
  def fork_state(self) -> bool:
45
53
  return self._fork_state
@@ -52,6 +60,9 @@ class Port:
52
60
  if isinstance(other, set) or isinstance(other, Graph):
53
61
  return Graph.from_port(self) >> other
54
62
 
63
+ if isinstance(other, Port):
64
+ return Graph.from_port(self) >> Graph.from_port(other)
65
+
55
66
  edge = Edge(from_port=self, to_node=other)
56
67
  if edge not in self._edges:
57
68
  self._edges.append(edge)
@@ -264,7 +264,6 @@ class WorkflowRunner(Generic[StateType]):
264
264
  )
265
265
  )
266
266
 
267
- invoked_ports = ports(outputs, node.state)
268
267
  node.state.meta.node_execution_cache.fulfill_node_execution(node.__class__, span_id)
269
268
 
270
269
  for descriptor, output_value in outputs:
@@ -275,6 +274,7 @@ class WorkflowRunner(Generic[StateType]):
275
274
 
276
275
  node.state.meta.node_outputs[descriptor] = output_value
277
276
 
277
+ invoked_ports = ports(outputs, node.state)
278
278
  self._workflow_event_inner_queue.put(
279
279
  NodeExecutionFulfilledEvent(
280
280
  trace_id=node.state.meta.trace_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.11.3
3
+ Version: 0.11.5
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -2,17 +2,17 @@ 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
@@ -30,7 +30,7 @@ vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nmPLj8vkbVCS46XQqmHq
30
30
  vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=4SSQGecKWHuoGy5YIGJeOZVHGKwTs_8Y-gf3GvsHb0M,8506
31
31
  vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=MASV7u0X4eiS3SzRWeBcbWZ_YgNlwwg5cM2vaFWjpqk,3915
32
32
  vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=gUbSP8_oSAMNIb0CGiefd2FMYgoO6wMoG6iA1FakMjk,13293
33
- vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=1pGC_P-y14g0hJRh4GUq99xu48JsfoPMMwtIsI3x3Tk,2078
33
+ vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=ygTjSjYDI4DtkxADWub5rhBnRWItMKWF6fezBrgpOKA,1979
34
34
  vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=UezalObmZ3mcg7Nou2RgiI_0cmc7_tSdZLNB591iCcI,2772
35
35
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=3TJvHX_Uuf_gr94VkYc_zmNH8I5p71ChIeoAbJZ3ddY,2158
36
36
  vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=sj-pySLVYGt032debhcQhHc5JwrALQrNCEKh3DXc8F8,7386
@@ -39,20 +39,20 @@ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=AqUlItgSZij12qRKguKV
39
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
- vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=4KHb2icxH_vHF1GvGp_9ar_flSWthXrGZk6zMa17J_8,8430
42
+ vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=BQEqi6_65h7PmP-FhBF0zUB3MBLA65DkZLU9IdS3iyA,8668
43
43
  vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=zOp4voBSgB3MR1R93wTOrsiiara_hxEAYFupLl_SvTA,2657
44
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
48
- vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=OlkXZASm1Zb3mLf38gd-NXcijtVn4UFptsRq-OFzArA,3882
48
+ vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=golxvSM6HIZ4uYKbfcI7muVJuckFZQcGFCAFvfx4Cls,4654
49
49
  vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=TEg3QbdE7rLbEhml9pMWmay--phsekGlfGVhTblxCGE,1727
51
51
  vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=e__ae2yepB5vlgVT08sr1DDB8pYjax6VQLo5FtRk-nA,17934
53
53
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=0McMPIPnIwE8iGh7fzFZL3dc6Q7NCQ_wUVFpl14YATo,22846
54
54
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py,sha256=d6xWDvi-Uo1KcMHVj_e8TujKhTwMKXAlT8H3P2V0gQU,53693
55
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=JV9TYz9BPVojWQ8ypGLsSfl9LdntDcxlm9OilP4qv6o,6874
55
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=MGwIwhCBCVYZmE_8Srts3fEs0BcRqXFFVbqiHiBQ55Q,6798
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
@@ -78,7 +78,7 @@ 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=XbAMOMcjdM9aXRo9_hecrehfft9oYmtBqckaIR0GAog,1890
81
+ vellum/client/core/client_wrapper.py,sha256=Lchp9CqqaSiM_CP0YETtEwr4rvjUV6p2Y7s0tL_-n6Q,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
@@ -1220,7 +1220,7 @@ vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyu
1220
1220
  vellum/workflows/events/node.py,sha256=3_NPVhXR-XMnNj5QVA_ycebs_-jRuGSLf5H7XjLQh8o,5258
1221
1221
  vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1222
1222
  vellum/workflows/events/tests/test_event.py,sha256=IBP_fCT_kPDdSCdgDCo7eaG7MYUR6aF4EFxwFIr9lbA,12717
1223
- vellum/workflows/events/types.py,sha256=RtwwmupUS65en_mprGWmt_VHP22sMpErhIwwpgPmvME,2846
1223
+ vellum/workflows/events/types.py,sha256=7sBSMNstsibqq93Klu4A-GInzo4IB23xWel595Uyi_Y,3077
1224
1224
  vellum/workflows/events/workflow.py,sha256=02UZE4sBpWrG2A3xddu8v0S9kPfo7YetX6xTvzKg-2w,5218
1225
1225
  vellum/workflows/exceptions.py,sha256=Dc7mxstsaMDRmS91QEOiZCbZ1ZIRacnm0l5lQmC6WkA,401
1226
1226
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1251,15 +1251,15 @@ vellum/workflows/expressions/not_between.py,sha256=H2huRR95D9Qb7lCHmK7BcK-Ug-E1g
1251
1251
  vellum/workflows/expressions/not_in.py,sha256=OQkN5G1E6VoTDpoLvx7X3GbohLlqEAYHV0rVVUV7ow4,1049
1252
1252
  vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
1253
1253
  vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
1254
- vellum/workflows/graph/graph.py,sha256=etuop6xNRJcK8anQx-I31xaExw604f9wC-lRQFr2Xw8,4571
1254
+ vellum/workflows/graph/graph.py,sha256=rmPPs2gtQhaYIuZKETdWrdacgwQRvT6soj12UOl1Tm0,5316
1255
1255
  vellum/workflows/graph/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1256
- 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
1257
1257
  vellum/workflows/inputs/__init__.py,sha256=AbFEteIYEvCb14fM3EK7bhM-40-6s494rSlIhQ4Dsss,62
1258
1258
  vellum/workflows/inputs/base.py,sha256=1kMgr0WqCYdWUqgFvgSoAMw2067FAlgwhGXLgbIOrLY,2391
1259
1259
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1260
1260
  vellum/workflows/nodes/__init__.py,sha256=aVdQVv7Y3Ro3JlqXGpxwaU2zrI06plDHD2aumH5WUIs,1157
1261
1261
  vellum/workflows/nodes/bases/__init__.py,sha256=Ll1Ti6t3e_HKtGLsQTHAJevDmfo0QtfgPZUZ9FCRduI,140
1262
- vellum/workflows/nodes/bases/base.py,sha256=fKEycP9yqcHjH781d4Sg5NFHZFDsbco0nunrU3T1kjc,14025
1262
+ vellum/workflows/nodes/bases/base.py,sha256=atWeRf3BgTZpy5L3Mm47D6dca_sYKfSr5D3-fruwX_k,14056
1263
1263
  vellum/workflows/nodes/bases/base_subworkflow_node/__init__.py,sha256=0nkHQiFC4IpA1ZGx60XG0BLUWF6hwUpgqmS3ZrlFGhg,80
1264
1264
  vellum/workflows/nodes/bases/base_subworkflow_node/node.py,sha256=vC0gUBQewAUNtP3i2G0-LUpE_kY-r_ijBD_tS1XkQ1E,383
1265
1265
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1284,7 +1284,7 @@ vellum/workflows/nodes/core/templating_node/node.py,sha256=19OFvRimrp1YuUO7H4rU7
1284
1284
  vellum/workflows/nodes/core/templating_node/render.py,sha256=OpJp0NAH6qcEL6K9lxR0qjpFb75TYNttJR5iCos8tmg,1792
1285
1285
  vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=0BtXeSix7KGIuKzlPFTMLATpNnFPhut1UV_srGptkt0,1120
1286
1286
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1287
- vellum/workflows/nodes/core/try_node/node.py,sha256=5LHTPFsJHZ0eZZbXdmNswj1sLs93W8Pa1dp6JSZBnPM,6538
1287
+ vellum/workflows/nodes/core/try_node/node.py,sha256=XS-xZaJ_tM3sS-FjWU-FCk9x4Id2lrniyknORgg0KjA,6654
1288
1288
  vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1289
1289
  vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=iD_ZjgB-v7pOYS6VjsqC-FWAFw8xvnEb-xXeau1Cuk0,4053
1290
1290
  vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDXl0Nmz4QzJV2PYe5RI,1023
@@ -1334,7 +1334,7 @@ vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfv
1334
1334
  vellum/workflows/outputs/base.py,sha256=a7W6rNSDSawwGAXYjNTF2iHb9lnZu7WFSOagZIyy__k,7976
1335
1335
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
1336
1336
  vellum/workflows/ports/node_ports.py,sha256=g4A-8iUAvEJSkaWppbvzAR8XU02R9U-qLN4rP2Kq4Aw,2743
1337
- vellum/workflows/ports/port.py,sha256=iLYzjIoFk5HWpNyhySf8E1uIuJ3MbAeJGD3yOnnLY70,2843
1337
+ vellum/workflows/ports/port.py,sha256=rc3GB7dDQCUs0IbY08a92-31YzJHQgBeww13brSJ2Js,3172
1338
1338
  vellum/workflows/ports/utils.py,sha256=pEjVNJKw9LhD_cFN-o0MWBOW2ejno7jv26qqzjLxwS4,1662
1339
1339
  vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcvEcsUBrzLIs,591
1340
1340
  vellum/workflows/references/environment_variable.py,sha256=7FFtiKfc4eyVkkfUbhc666OBNDqvFlMoNQEYmGpEVVE,661
@@ -1350,7 +1350,7 @@ vellum/workflows/references/workflow_input.py,sha256=epspVRZ9n_nxoTxI5Am3GDd2fpU
1350
1350
  vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
1351
1351
  vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
1352
1352
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
1353
- vellum/workflows/runner/runner.py,sha256=TtCB2hLnAU83mmR17qzfHQPfJfpUMqi3Lqq0q1viUIQ,27573
1353
+ vellum/workflows/runner/runner.py,sha256=7mSVaqJjyVljuuy3-bIO56mzXXMeQwKcYzZOuo8aC8E,27573
1354
1354
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1355
1355
  vellum/workflows/state/base.py,sha256=jpSzF1OQd3-fqi6dMGlNsQl-7JnJxCdzWIigmX8Wz-I,14425
1356
1356
  vellum/workflows/state/context.py,sha256=oXiEdNsWJi1coRB85IreTgUeR6_CrWWBXndtLff9S7M,1272
@@ -1377,8 +1377,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
1377
1377
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1378
1378
  vellum/workflows/workflows/base.py,sha256=mnI-kZ78yt7u6NFSTUo-tYjDnarP-RJ7uZjwjCn6PCQ,16795
1379
1379
  vellum/workflows/workflows/event_filters.py,sha256=-uQcMB7IpPd-idMku8f2QNVhPXPFWo6FZLlGjRf8rCo,1996
1380
- vellum_ai-0.11.3.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1381
- vellum_ai-0.11.3.dist-info/METADATA,sha256=6F3pwCqaSVevjDTq4ErE748xs0VspO_aHNYnoOSq-dY,5128
1382
- vellum_ai-0.11.3.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
1383
- vellum_ai-0.11.3.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1384
- vellum_ai-0.11.3.dist-info/RECORD,,
1380
+ vellum_ai-0.11.5.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1381
+ vellum_ai-0.11.5.dist-info/METADATA,sha256=8CXclMT8Nu3M7czzPkH0K0toXno3RpXdZIBlhrasKME,5128
1382
+ vellum_ai-0.11.5.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
1383
+ vellum_ai-0.11.5.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1384
+ vellum_ai-0.11.5.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("."))
@@ -39,7 +39,6 @@ class BaseErrorNodeDisplay(BaseNodeVellumDisplay[_ErrorNodeType], Generic[_Error
39
39
  "data": {
40
40
  "name": self.name,
41
41
  "label": self.label,
42
- "source_handle_id": str(self.get_source_handle_id(display_context.port_displays)),
43
42
  "target_handle_id": str(self.get_target_handle_id()),
44
43
  "error_source_input_id": str(error_source_input_id),
45
44
  "error_output_id": str(self.error_output_id),
@@ -15,7 +15,7 @@ from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
15
15
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
16
16
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
17
17
  from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
18
- from vellum_ee.workflows.display.vellum import NodeInput
18
+ from vellum_ee.workflows.display.vellum import InputVariablePointer, NodeInput
19
19
 
20
20
  _SearchNodeType = TypeVar("_SearchNodeType", bound=SearchNode)
21
21
 
@@ -28,7 +28,7 @@ class VariableIdMap:
28
28
 
29
29
 
30
30
  class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_SearchNodeType]):
31
- variable_ids: Optional[VariableIdMap] = None
31
+ input_variable_ids_by_logical_id: Optional[Dict[str, str]] = None
32
32
 
33
33
  def serialize(
34
34
  self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
@@ -128,7 +128,6 @@ class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_Sea
128
128
  logical_expression: Union[VellumValueLogicalConditionGroupRequest, VellumValueLogicalConditionRequest],
129
129
  display_context: WorkflowDisplayContext,
130
130
  path: List[int] = [],
131
- variable_id_map: Optional[VariableIdMap] = None,
132
131
  ) -> Tuple[JsonObject, List[NodeInput]]:
133
132
  if isinstance(logical_expression, VellumValueLogicalConditionGroupRequest):
134
133
  conditions: JsonArray = []
@@ -150,38 +149,42 @@ class BaseSearchNodeDisplay(BaseNodeVellumDisplay[_SearchNodeType], Generic[_Sea
150
149
  variables,
151
150
  )
152
151
  elif isinstance(logical_expression, VellumValueLogicalConditionRequest):
153
- lhs_variable_id = (
154
- variable_id_map.lhs.id
155
- if variable_id_map and variable_id_map.lhs and variable_id_map.lhs.id
156
- else uuid4_from_hash(f"{self.node_id}|{hash(tuple(path))}|lhs")
152
+ lhs_variable_id = str(logical_expression.lhs_variable.value)
153
+ rhs_variable_id = str(logical_expression.rhs_variable.value)
154
+ lhs_query_input_id = (
155
+ self.input_variable_ids_by_logical_id[lhs_variable_id]
156
+ if self.input_variable_ids_by_logical_id
157
+ else uuid4_from_hash(f"{self.node_id}|{hash(path)}")
157
158
  )
158
- rhs_variable_id = (
159
- variable_id_map.rhs.id
160
- if variable_id_map and variable_id_map.rhs and variable_id_map.rhs.id
161
- else uuid4_from_hash(f"{self.node_id}|{hash(tuple(path))}|rhs")
159
+ rhs_query_input_id = (
160
+ self.input_variable_ids_by_logical_id[rhs_variable_id]
161
+ if self.input_variable_ids_by_logical_id
162
+ else uuid4_from_hash(f"{self.node_id}|{hash(path)}")
162
163
  )
163
164
 
164
165
  return (
165
166
  {
166
167
  "type": "LOGICAL_CONDITION",
167
- "lhs": str(lhs_variable_id),
168
+ "lhs_variable_id": str(lhs_variable_id),
168
169
  "operator": logical_expression.operator,
169
- "rhs": str(rhs_variable_id),
170
+ "rhs_variable_id": str(rhs_variable_id),
170
171
  },
171
172
  [
172
173
  create_node_input(
173
174
  self.node_id,
174
175
  f"vellum-query-builder-variable-{lhs_variable_id}",
175
- logical_expression.lhs_variable.value,
176
+ lhs_query_input_id,
176
177
  display_context,
177
- input_id=lhs_variable_id,
178
+ input_id=UUID(lhs_variable_id),
179
+ pointer_type=InputVariablePointer,
178
180
  ),
179
181
  create_node_input(
180
182
  self.node_id,
181
183
  f"vellum-query-builder-variable-{rhs_variable_id}",
182
- logical_expression.rhs_variable.value,
184
+ rhs_query_input_id,
183
185
  display_context,
184
- input_id=rhs_variable_id,
186
+ input_id=UUID(rhs_variable_id),
187
+ pointer_type=InputVariablePointer,
185
188
  ),
186
189
  ],
187
190
  )
@@ -10,6 +10,10 @@ from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
10
10
  from vellum_ee.workflows.display.utils.vellum import create_node_input_value_pointer_rule, primitive_to_vellum_value
11
11
  from vellum_ee.workflows.display.vellum import (
12
12
  ConstantValuePointer,
13
+ ExecutionCounterData,
14
+ ExecutionCounterPointer,
15
+ InputVariableData,
16
+ InputVariablePointer,
13
17
  NodeInput,
14
18
  NodeInputValuePointer,
15
19
  NodeInputValuePointerRule,
@@ -93,4 +97,15 @@ def create_pointer(
93
97
  )
94
98
 
95
99
  vellum_variable_value = primitive_to_vellum_value(value)
96
- return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_variable_value)
100
+ if pointer_type is InputVariablePointer:
101
+ return InputVariablePointer(type="INPUT_VARIABLE", data=InputVariableData(input_variable_id=value))
102
+ elif pointer_type is WorkspaceSecretPointer:
103
+ return WorkspaceSecretPointer(
104
+ type="WORKSPACE_SECRET", data=WorkspaceSecretData(type="STRING", workspace_secret_id=value)
105
+ )
106
+ elif pointer_type is ExecutionCounterPointer:
107
+ return ExecutionCounterPointer(type="EXECUTION_COUNTER", data=ExecutionCounterData(node_id=value))
108
+ elif pointer_type is ConstantValuePointer or pointer_type is None:
109
+ return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_variable_value)
110
+ else:
111
+ raise ValueError(f"Pointer type {pointer_type} not supported")
@@ -102,7 +102,6 @@ def test_serialize_workflow():
102
102
  "data": {
103
103
  "name": "error-node",
104
104
  "label": "Fail Node",
105
- "source_handle_id": "ca17d318-a0f5-4f7c-be6c-59c9dc1dd7ed",
106
105
  "target_handle_id": "70c19f1c-309c-4a5d-ba65-664c0bb2fedf",
107
106
  "error_source_input_id": "None",
108
107
  "error_output_id": "None",