vellum-ai 1.11.2__py3-none-any.whl → 1.13.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.

Potentially problematic release.


This version of vellum-ai might be problematic. Click here for more details.

Files changed (275) hide show
  1. vellum/__init__.py +18 -0
  2. vellum/client/README.md +1 -1
  3. vellum/client/core/client_wrapper.py +2 -2
  4. vellum/client/core/force_multipart.py +4 -2
  5. vellum/client/core/http_response.py +1 -1
  6. vellum/client/core/pydantic_utilities.py +7 -4
  7. vellum/client/errors/too_many_requests_error.py +1 -2
  8. vellum/client/reference.md +677 -76
  9. vellum/client/resources/container_images/client.py +299 -0
  10. vellum/client/resources/container_images/raw_client.py +286 -0
  11. vellum/client/resources/documents/client.py +20 -10
  12. vellum/client/resources/documents/raw_client.py +20 -10
  13. vellum/client/resources/events/raw_client.py +4 -4
  14. vellum/client/resources/integration_auth_configs/client.py +2 -0
  15. vellum/client/resources/integration_auth_configs/raw_client.py +2 -0
  16. vellum/client/resources/integration_providers/client.py +28 -2
  17. vellum/client/resources/integration_providers/raw_client.py +24 -0
  18. vellum/client/resources/integrations/client.py +52 -4
  19. vellum/client/resources/integrations/raw_client.py +61 -0
  20. vellum/client/resources/workflow_deployments/client.py +156 -0
  21. vellum/client/resources/workflow_deployments/raw_client.py +334 -0
  22. vellum/client/resources/workflows/client.py +212 -8
  23. vellum/client/resources/workflows/raw_client.py +343 -6
  24. vellum/client/types/__init__.py +18 -0
  25. vellum/client/types/api_actor_type_enum.py +1 -1
  26. vellum/client/types/check_workflow_execution_status_error.py +21 -0
  27. vellum/client/types/check_workflow_execution_status_response.py +29 -0
  28. vellum/client/types/code_execution_package_request.py +21 -0
  29. vellum/client/types/composio_execute_tool_request.py +5 -0
  30. vellum/client/types/composio_tool_definition.py +1 -0
  31. vellum/client/types/container_image_build_config.py +1 -0
  32. vellum/client/types/container_image_container_image_tag.py +1 -0
  33. vellum/client/types/dataset_row_push_request.py +3 -0
  34. vellum/client/types/document_document_to_document_index.py +1 -0
  35. vellum/client/types/integration_name.py +24 -0
  36. vellum/client/types/node_execution_fulfilled_body.py +1 -0
  37. vellum/client/types/node_execution_log_body.py +24 -0
  38. vellum/client/types/node_execution_log_event.py +47 -0
  39. vellum/client/types/prompt_deployment_release_prompt_deployment.py +1 -0
  40. vellum/client/types/runner_config_request.py +24 -0
  41. vellum/client/types/severity_enum.py +5 -0
  42. vellum/client/types/slim_composio_tool_definition.py +1 -0
  43. vellum/client/types/slim_document_document_to_document_index.py +2 -0
  44. vellum/client/types/type_checker_enum.py +5 -0
  45. vellum/client/types/vellum_audio.py +5 -1
  46. vellum/client/types/vellum_audio_request.py +5 -1
  47. vellum/client/types/vellum_document.py +5 -1
  48. vellum/client/types/vellum_document_request.py +5 -1
  49. vellum/client/types/vellum_image.py +5 -1
  50. vellum/client/types/vellum_image_request.py +5 -1
  51. vellum/client/types/vellum_node_execution_event.py +2 -0
  52. vellum/client/types/vellum_variable.py +5 -0
  53. vellum/client/types/vellum_variable_extensions.py +1 -0
  54. vellum/client/types/vellum_variable_type.py +1 -0
  55. vellum/client/types/vellum_video.py +5 -1
  56. vellum/client/types/vellum_video_request.py +5 -1
  57. vellum/client/types/workflow_deployment_release_workflow_deployment.py +1 -0
  58. vellum/client/types/workflow_event.py +2 -0
  59. vellum/client/types/workflow_execution_fulfilled_body.py +1 -0
  60. vellum/client/types/workflow_result_event_output_data_array.py +1 -1
  61. vellum/client/types/workflow_result_event_output_data_chat_history.py +1 -1
  62. vellum/client/types/workflow_result_event_output_data_error.py +1 -1
  63. vellum/client/types/workflow_result_event_output_data_function_call.py +1 -1
  64. vellum/client/types/workflow_result_event_output_data_json.py +1 -1
  65. vellum/client/types/workflow_result_event_output_data_number.py +1 -1
  66. vellum/client/types/workflow_result_event_output_data_search_results.py +1 -1
  67. vellum/client/types/workflow_result_event_output_data_string.py +1 -1
  68. vellum/client/types/workflow_sandbox_execute_node_response.py +8 -0
  69. vellum/plugins/vellum_mypy.py +37 -2
  70. vellum/types/check_workflow_execution_status_error.py +3 -0
  71. vellum/types/check_workflow_execution_status_response.py +3 -0
  72. vellum/types/code_execution_package_request.py +3 -0
  73. vellum/types/node_execution_log_body.py +3 -0
  74. vellum/types/node_execution_log_event.py +3 -0
  75. vellum/types/runner_config_request.py +3 -0
  76. vellum/types/severity_enum.py +3 -0
  77. vellum/types/type_checker_enum.py +3 -0
  78. vellum/types/workflow_sandbox_execute_node_response.py +3 -0
  79. vellum/utils/files/mixin.py +26 -0
  80. vellum/utils/files/tests/test_mixin.py +62 -0
  81. vellum/utils/tests/test_vellum_client.py +95 -0
  82. vellum/utils/uuid.py +19 -2
  83. vellum/utils/vellum_client.py +10 -3
  84. vellum/workflows/__init__.py +7 -1
  85. vellum/workflows/descriptors/base.py +86 -0
  86. vellum/workflows/descriptors/tests/test_utils.py +9 -0
  87. vellum/workflows/errors/tests/__init__.py +0 -0
  88. vellum/workflows/errors/tests/test_types.py +52 -0
  89. vellum/workflows/errors/types.py +1 -0
  90. vellum/workflows/events/node.py +24 -0
  91. vellum/workflows/events/tests/test_event.py +123 -0
  92. vellum/workflows/events/types.py +2 -1
  93. vellum/workflows/events/workflow.py +28 -2
  94. vellum/workflows/expressions/add.py +3 -0
  95. vellum/workflows/expressions/tests/test_add.py +24 -0
  96. vellum/workflows/graph/graph.py +26 -5
  97. vellum/workflows/graph/tests/test_graph.py +228 -1
  98. vellum/workflows/inputs/base.py +22 -6
  99. vellum/workflows/inputs/dataset_row.py +121 -16
  100. vellum/workflows/inputs/tests/test_inputs.py +3 -3
  101. vellum/workflows/integrations/tests/test_vellum_integration_service.py +84 -0
  102. vellum/workflows/integrations/vellum_integration_service.py +12 -1
  103. vellum/workflows/loaders/base.py +2 -0
  104. vellum/workflows/nodes/bases/base.py +37 -16
  105. vellum/workflows/nodes/bases/tests/test_base_node.py +104 -1
  106. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -0
  107. vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +1 -1
  108. vellum/workflows/nodes/core/map_node/node.py +7 -5
  109. vellum/workflows/nodes/core/map_node/tests/test_node.py +33 -0
  110. vellum/workflows/nodes/core/retry_node/node.py +1 -0
  111. vellum/workflows/nodes/core/try_node/node.py +1 -0
  112. vellum/workflows/nodes/displayable/api_node/node.py +3 -2
  113. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +38 -0
  114. vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
  115. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +18 -1
  116. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +109 -2
  117. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +13 -2
  118. vellum/workflows/nodes/displayable/code_execution_node/node.py +9 -15
  119. vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +65 -24
  120. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -0
  121. vellum/workflows/nodes/displayable/final_output_node/node.py +24 -69
  122. vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +53 -3
  123. vellum/workflows/nodes/displayable/note_node/node.py +4 -1
  124. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +16 -5
  125. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +47 -0
  126. vellum/workflows/nodes/displayable/tool_calling_node/node.py +74 -34
  127. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +204 -8
  128. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +92 -71
  129. vellum/workflows/nodes/mocks.py +47 -213
  130. vellum/workflows/nodes/tests/test_mocks.py +0 -177
  131. vellum/workflows/nodes/utils.py +23 -8
  132. vellum/workflows/outputs/base.py +36 -3
  133. vellum/workflows/references/environment_variable.py +1 -11
  134. vellum/workflows/references/lazy.py +8 -0
  135. vellum/workflows/references/state_value.py +24 -1
  136. vellum/workflows/references/tests/test_lazy.py +58 -0
  137. vellum/workflows/references/trigger.py +8 -3
  138. vellum/workflows/references/workflow_input.py +8 -0
  139. vellum/workflows/resolvers/resolver.py +13 -3
  140. vellum/workflows/resolvers/tests/test_resolver.py +31 -0
  141. vellum/workflows/runner/runner.py +159 -14
  142. vellum/workflows/runner/tests/__init__.py +0 -0
  143. vellum/workflows/runner/tests/test_runner.py +170 -0
  144. vellum/workflows/sandbox.py +7 -8
  145. vellum/workflows/state/base.py +89 -30
  146. vellum/workflows/state/context.py +74 -3
  147. vellum/workflows/state/tests/test_state.py +269 -1
  148. vellum/workflows/tests/test_dataset_row.py +8 -7
  149. vellum/workflows/tests/test_sandbox.py +97 -8
  150. vellum/workflows/triggers/__init__.py +2 -1
  151. vellum/workflows/triggers/base.py +160 -28
  152. vellum/workflows/triggers/chat_message.py +141 -0
  153. vellum/workflows/triggers/integration.py +12 -0
  154. vellum/workflows/triggers/manual.py +3 -1
  155. vellum/workflows/triggers/schedule.py +3 -1
  156. vellum/workflows/triggers/tests/test_chat_message.py +257 -0
  157. vellum/workflows/types/core.py +18 -0
  158. vellum/workflows/types/definition.py +6 -13
  159. vellum/workflows/types/generics.py +12 -0
  160. vellum/workflows/types/tests/test_utils.py +12 -0
  161. vellum/workflows/types/utils.py +32 -2
  162. vellum/workflows/types/workflow_metadata.py +124 -0
  163. vellum/workflows/utils/functions.py +152 -16
  164. vellum/workflows/utils/pydantic_schema.py +19 -1
  165. vellum/workflows/utils/tests/test_functions.py +123 -8
  166. vellum/workflows/utils/tests/test_validate.py +79 -0
  167. vellum/workflows/utils/tests/test_vellum_variables.py +62 -2
  168. vellum/workflows/utils/uuids.py +90 -0
  169. vellum/workflows/utils/validate.py +108 -0
  170. vellum/workflows/utils/vellum_variables.py +96 -16
  171. vellum/workflows/workflows/base.py +177 -35
  172. vellum/workflows/workflows/tests/test_base_workflow.py +51 -0
  173. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/METADATA +6 -1
  174. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/RECORD +274 -227
  175. vellum_cli/__init__.py +21 -0
  176. vellum_cli/config.py +16 -2
  177. vellum_cli/pull.py +2 -0
  178. vellum_cli/push.py +23 -10
  179. vellum_cli/tests/conftest.py +8 -13
  180. vellum_cli/tests/test_image_push.py +4 -11
  181. vellum_cli/tests/test_pull.py +83 -68
  182. vellum_cli/tests/test_push.py +251 -2
  183. vellum_ee/assets/node-definitions.json +225 -12
  184. vellum_ee/scripts/generate_node_definitions.py +15 -3
  185. vellum_ee/workflows/display/base.py +4 -3
  186. vellum_ee/workflows/display/nodes/base_node_display.py +44 -11
  187. vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +93 -0
  188. vellum_ee/workflows/display/nodes/types.py +1 -0
  189. vellum_ee/workflows/display/nodes/vellum/__init__.py +0 -2
  190. vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +5 -2
  191. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  192. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +10 -2
  193. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +17 -14
  194. vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
  195. vellum_ee/workflows/display/nodes/vellum/note_node.py +18 -3
  196. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +37 -14
  197. vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +62 -2
  198. vellum_ee/workflows/display/nodes/vellum/tests/test_final_output_node.py +136 -0
  199. vellum_ee/workflows/display/nodes/vellum/tests/test_note_node.py +44 -7
  200. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +5 -13
  201. vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +27 -17
  202. vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +145 -22
  203. vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +107 -2
  204. vellum_ee/workflows/display/nodes/vellum/utils.py +54 -12
  205. vellum_ee/workflows/display/tests/test_base_workflow_display.py +13 -16
  206. vellum_ee/workflows/display/tests/test_json_schema_validation.py +190 -0
  207. vellum_ee/workflows/display/tests/test_mocks.py +912 -0
  208. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +14 -2
  209. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +109 -0
  210. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +3 -0
  211. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +187 -1
  212. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +34 -325
  213. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +42 -393
  214. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +13 -315
  215. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +2 -122
  216. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +24 -115
  217. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +4 -93
  218. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +7 -80
  219. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +9 -101
  220. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +77 -308
  221. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +62 -324
  222. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -82
  223. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +4 -142
  224. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +1 -61
  225. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_set_state_node_serialization.py +4 -4
  226. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +205 -134
  227. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +34 -146
  228. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +2 -0
  229. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +8 -6
  230. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +137 -266
  231. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_tool_wrapper_serialization.py +84 -0
  232. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +55 -16
  233. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +15 -1
  234. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_tool_wrapper_serialization.py +71 -0
  235. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_vellum_integration_serialization.py +119 -0
  236. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +1 -1
  237. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +0 -2
  238. vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_dict_reference_serialization.py +22 -1
  239. vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_trigger_serialization.py +412 -0
  240. vellum_ee/workflows/display/tests/workflow_serialization/test_code_tool_node_reference_error.py +106 -0
  241. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +9 -41
  242. vellum_ee/workflows/display/tests/workflow_serialization/test_duplicate_trigger_name_validation.py +208 -0
  243. vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_not_referenced_by_workflow_outputs.py +45 -0
  244. vellum_ee/workflows/display/tests/workflow_serialization/test_infinite_loop_validation.py +66 -0
  245. vellum_ee/workflows/display/tests/workflow_serialization/test_int_input_serialization.py +40 -0
  246. vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_serialization.py +8 -14
  247. vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_validation.py +173 -0
  248. vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_with_entrypoint_node_id.py +16 -13
  249. vellum_ee/workflows/display/tests/workflow_serialization/test_list_vellum_document_serialization.py +5 -1
  250. vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +12 -2
  251. vellum_ee/workflows/display/tests/workflow_serialization/test_multi_trigger_same_node_serialization.py +111 -0
  252. vellum_ee/workflows/display/tests/workflow_serialization/test_no_triggers_no_entrypoint_validation.py +64 -0
  253. vellum_ee/workflows/display/tests/workflow_serialization/test_partial_workflow_meta_display_override.py +55 -0
  254. vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_dataset_mocks_serialization.py +268 -0
  255. vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_invalid_pdf_data_url.py +49 -0
  256. vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_validation_errors.py +112 -0
  257. vellum_ee/workflows/display/tests/workflow_serialization/test_scheduled_trigger_serialization.py +25 -16
  258. vellum_ee/workflows/display/tests/workflow_serialization/test_terminal_node_in_unused_graphs_serialization.py +53 -0
  259. vellum_ee/workflows/display/utils/exceptions.py +34 -0
  260. vellum_ee/workflows/display/utils/expressions.py +463 -52
  261. vellum_ee/workflows/display/utils/metadata.py +98 -33
  262. vellum_ee/workflows/display/utils/tests/test_metadata.py +31 -0
  263. vellum_ee/workflows/display/utils/triggers.py +153 -0
  264. vellum_ee/workflows/display/utils/vellum.py +59 -5
  265. vellum_ee/workflows/display/workflows/base_workflow_display.py +656 -254
  266. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +26 -0
  267. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +77 -29
  268. vellum_ee/workflows/server/namespaces.py +18 -0
  269. vellum_ee/workflows/tests/test_display_meta.py +2 -0
  270. vellum_ee/workflows/tests/test_serialize_module.py +174 -7
  271. vellum_ee/workflows/tests/test_server.py +0 -3
  272. vellum_ee/workflows/display/nodes/vellum/function_node.py +0 -14
  273. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/LICENSE +0 -0
  274. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/WHEEL +0 -0
  275. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/entry_points.txt +0 -0
@@ -183,8 +183,26 @@ def test_tool_calling_node_inline_workflow_context():
183
183
  tool_prompt_node=tool_prompt_node,
184
184
  )
185
185
 
186
+ # AND we create a state with a function call
187
+ state = ToolCallingState(
188
+ meta=StateMeta(
189
+ node_outputs={
190
+ tool_prompt_node.Outputs.results: [
191
+ FunctionCallVellumValue(
192
+ value=FunctionCall(
193
+ arguments={},
194
+ id="call_test",
195
+ name="MyWorkflow",
196
+ state="FULFILLED",
197
+ ),
198
+ )
199
+ ],
200
+ },
201
+ )
202
+ )
203
+
186
204
  # AND we create an instance with a context containing generated_files
187
- function_node = function_node_class()
205
+ function_node = function_node_class(state=state)
188
206
 
189
207
  # Create a parent context with test data
190
208
  parent_context = WorkflowContext(
@@ -192,6 +210,12 @@ def test_tool_calling_node_inline_workflow_context():
192
210
  )
193
211
  function_node._context = parent_context
194
212
 
213
+ # AND the _inputs should be populated with resolved values from state
214
+ assert function_node._inputs == {
215
+ function_node_class.arguments: {},
216
+ function_node_class.function_call_id: "call_test",
217
+ }
218
+
195
219
  # WHEN the function node runs
196
220
  outputs = list(function_node.run())
197
221
 
@@ -558,11 +582,10 @@ def test_mcp_node_outputs_result():
558
582
  parameters={},
559
583
  )
560
584
 
561
- # AND a tool prompt node
562
585
  tool_prompt_node = create_tool_prompt_node(
563
586
  ml_model="test-model",
564
587
  blocks=[],
565
- functions=[mcp_server],
588
+ functions=[mcp_tool],
566
589
  prompt_inputs=None,
567
590
  parameters=DEFAULT_PROMPT_PARAMETERS,
568
591
  )
@@ -677,9 +700,93 @@ def test_vellum_integration_node_outputs_result(vellum_client):
677
700
  assert result_output.value == {"id": 123, "url": "https://github.com/test-owner/test-repo/issues/123"}
678
701
 
679
702
 
680
- def test_tool_calling_node_400_error_returns_internal_error(vellum_adhoc_prompt_client):
703
+ def test_vellum_integration_node_error_outputs_result(vellum_client):
704
+ """Test that VellumIntegrationNode yields error payload as result output when NodeException occurs."""
705
+
706
+ # GIVEN a VellumIntegrationToolDefinition
707
+ github_tool = VellumIntegrationToolDefinition(
708
+ provider=VellumIntegrationProviderType.COMPOSIO,
709
+ integration_name="GITHUB",
710
+ name="create_issue",
711
+ description="Create a new issue in a GitHub repository",
712
+ )
713
+
714
+ # AND a tool prompt node
715
+ tool_prompt_node = create_tool_prompt_node(
716
+ ml_model="test-model",
717
+ blocks=[],
718
+ functions=[github_tool],
719
+ prompt_inputs=None,
720
+ parameters=DEFAULT_PROMPT_PARAMETERS,
721
+ )
722
+
723
+ function_node_class = create_function_node(
724
+ function=github_tool,
725
+ tool_prompt_node=tool_prompt_node,
726
+ )
727
+
728
+ # AND a state with a function call
729
+ state = ToolCallingState(
730
+ meta=StateMeta(
731
+ node_outputs={
732
+ tool_prompt_node.Outputs.results: [
733
+ FunctionCallVellumValue(
734
+ value=FunctionCall(
735
+ arguments={"owner": "test-owner", "repo": "test-repo", "title": "Test Issue"},
736
+ id="call_vellum_error_test",
737
+ name="create_issue",
738
+ state="FULFILLED",
739
+ ),
740
+ )
741
+ ],
742
+ },
743
+ )
744
+ )
745
+
746
+ # AND mock the vellum client's integration service to raise a 500 error
747
+ vellum_client.integrations.execute_integration_tool.side_effect = ApiError(
748
+ status_code=500,
749
+ body={"detail": "Internal server error occurred while executing the tool."},
750
+ )
751
+
752
+ # AND create a context with the vellum client
753
+ context = WorkflowContext()
754
+ context.vellum_client = vellum_client
755
+
756
+ function_node = function_node_class(state=state)
757
+ function_node._context = context
758
+
759
+ # WHEN the VellumIntegration node runs
760
+ outputs = list(function_node.run())
761
+
762
+ # THEN there should be exactly one output with name "result"
763
+ assert len(outputs) == 1
764
+ result_output = outputs[0]
765
+ assert isinstance(result_output, BaseOutput)
766
+ assert result_output.name == "result"
767
+ assert result_output.is_fulfilled is True
768
+
769
+ # AND the result should contain the error payload
770
+ error_result = result_output.value
771
+ assert isinstance(error_result, dict)
772
+ assert "code" in error_result
773
+ assert "message" in error_result
774
+ assert error_result["code"] == "PROVIDER_ERROR"
775
+ assert "Internal server error occurred while executing the tool" in error_result["message"]
776
+
777
+ # AND the error should also be in chat history
778
+ assert len(state.chat_history) == 1
779
+ function_message = state.chat_history[0]
780
+ assert function_message.role == "FUNCTION"
781
+ assert isinstance(function_message.content, StringChatMessageContent)
782
+ error_data = json.loads(function_message.content.value)
783
+ assert "error" in error_data
784
+ assert error_data["error"]["code"] == "PROVIDER_ERROR"
785
+
786
+
787
+ def test_tool_calling_node_400_error_preserves_invalid_inputs(vellum_adhoc_prompt_client):
681
788
  """
682
- Test that ToolCallingNode returns INTERNAL_ERROR when the underlying prompt node returns a 400 error.
789
+ Test that ToolCallingNode preserves INVALID_INPUTS error code when the underlying prompt node returns a 400 error.
683
790
  """
684
791
 
685
792
  # GIVEN a ToolCallingNode with minimal configuration
@@ -705,10 +812,10 @@ def test_tool_calling_node_400_error_returns_internal_error(vellum_adhoc_prompt_
705
812
  with pytest.raises(NodeException) as exc_info:
706
813
  list(node.run())
707
814
 
708
- # AND the error code should be INTERNAL_ERROR (not INVALID_INPUTS)
815
+ # THEN the error code should be INVALID_INPUTS (preserved from the underlying prompt node)
709
816
  e = exc_info.value
710
- assert e.code == WorkflowErrorCode.INTERNAL_ERROR
711
- assert e.message == "Internal server error"
817
+ assert e.code == WorkflowErrorCode.INVALID_INPUTS
818
+ assert e.message == "Invalid request parameters"
712
819
 
713
820
 
714
821
  def test_vellum_integration_node_500_error_feeds_back_to_model(vellum_adhoc_prompt_client, vellum_client):
@@ -800,3 +907,92 @@ def test_vellum_integration_node_500_error_feeds_back_to_model(vellum_adhoc_prom
800
907
 
801
908
  # AND the third message should be the assistant's response to the error
802
909
  assert chat_history[2].role == "ASSISTANT"
910
+
911
+
912
+ def test_tool_calling_node_json_output(vellum_adhoc_prompt_client):
913
+ """
914
+ Tests that ToolCallingNode exposes a json output when the LLM returns valid JSON.
915
+ """
916
+
917
+ # GIVEN a ToolCallingNode with a simple function
918
+ class TestToolCallingNode(ToolCallingNode):
919
+ ml_model = "gpt-4o-mini"
920
+ blocks = []
921
+ functions = [first_function]
922
+ max_prompt_iterations = 1
923
+
924
+ # AND the LLM returns a JSON response
925
+ expected_json = {"items": ["apple", "banana", "cherry"], "count": 3}
926
+
927
+ def generate_prompt_events(*args: Any, **kwargs: Any) -> Iterator[ExecutePromptEvent]:
928
+ execution_id = str(uuid4())
929
+ events: List[ExecutePromptEvent] = [
930
+ InitiatedExecutePromptEvent(execution_id=execution_id),
931
+ FulfilledExecutePromptEvent(
932
+ execution_id=execution_id,
933
+ outputs=[StringVellumValue(value=json.dumps(expected_json))],
934
+ ),
935
+ ]
936
+ yield from events
937
+
938
+ vellum_adhoc_prompt_client.adhoc_execute_prompt_stream.side_effect = generate_prompt_events
939
+
940
+ # WHEN the ToolCallingNode runs
941
+ state = BaseState()
942
+ node = TestToolCallingNode(state=state)
943
+ node_outputs = {}
944
+ for output in node.run():
945
+ if output.is_fulfilled:
946
+ node_outputs[output.name] = output.value
947
+
948
+ # THEN the json output should contain the parsed JSON
949
+ assert "json" in node_outputs
950
+ assert node_outputs["json"] == expected_json
951
+
952
+ # AND the text output should contain the raw JSON string
953
+ assert "text" in node_outputs
954
+ assert node_outputs["text"] == json.dumps(expected_json)
955
+
956
+
957
+ def test_tool_calling_node_json_output_not_present_for_non_json(vellum_adhoc_prompt_client):
958
+ """
959
+ Tests that ToolCallingNode does not expose a json output when the LLM returns non-JSON text.
960
+ """
961
+
962
+ # GIVEN a ToolCallingNode with a simple function
963
+ class TestToolCallingNode(ToolCallingNode):
964
+ ml_model = "gpt-4o-mini"
965
+ blocks = []
966
+ functions = [first_function]
967
+ max_prompt_iterations = 1
968
+
969
+ # AND the LLM returns a plain text response (not JSON)
970
+ plain_text_response = "Hello! I can help you with that."
971
+
972
+ def generate_prompt_events(*args: Any, **kwargs: Any) -> Iterator[ExecutePromptEvent]:
973
+ execution_id = str(uuid4())
974
+ events: List[ExecutePromptEvent] = [
975
+ InitiatedExecutePromptEvent(execution_id=execution_id),
976
+ FulfilledExecutePromptEvent(
977
+ execution_id=execution_id,
978
+ outputs=[StringVellumValue(value=plain_text_response)],
979
+ ),
980
+ ]
981
+ yield from events
982
+
983
+ vellum_adhoc_prompt_client.adhoc_execute_prompt_stream.side_effect = generate_prompt_events
984
+
985
+ # WHEN the ToolCallingNode runs
986
+ state = BaseState()
987
+ node = TestToolCallingNode(state=state)
988
+ node_outputs = {}
989
+ for output in node.run():
990
+ if output.is_fulfilled:
991
+ node_outputs[output.name] = output.value
992
+
993
+ # THEN the json output should not be present
994
+ assert "json" not in node_outputs
995
+
996
+ # AND the text output should contain the plain text
997
+ assert "text" in node_outputs
998
+ assert node_outputs["text"] == plain_text_response
@@ -35,14 +35,12 @@ from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
35
35
  from vellum.workflows.types.definition import (
36
36
  ComposioToolDefinition,
37
37
  DeploymentDefinition,
38
- MCPServer,
39
38
  MCPToolDefinition,
40
- Tool,
41
39
  ToolBase,
42
40
  VellumIntegrationToolDefinition,
43
41
  )
44
42
  from vellum.workflows.types.generics import is_workflow_class
45
- from vellum.workflows.utils.functions import compile_mcp_tool_definition, get_mcp_tool_name
43
+ from vellum.workflows.utils.functions import get_mcp_tool_name
46
44
 
47
45
  CHAT_HISTORY_VARIABLE = "chat_history"
48
46
 
@@ -53,7 +51,8 @@ logger = logging.getLogger(__name__)
53
51
  class FunctionCallNodeMixin:
54
52
  """Mixin providing common functionality for nodes that handle function calls."""
55
53
 
56
- function_call_output: List[PromptOutput]
54
+ arguments: dict
55
+ function_call_id: Optional[str]
57
56
 
58
57
  def _handle_tool_exception(self, e: Exception, tool_type: str, tool_name: str) -> None:
59
58
  """
@@ -77,27 +76,9 @@ class FunctionCallNodeMixin:
77
76
  code=WorkflowErrorCode.NODE_EXECUTION,
78
77
  ) from e
79
78
 
80
- def _extract_function_arguments(self) -> dict:
81
- """Extract arguments from function call output."""
82
- current_index = getattr(self, "state").current_prompt_output_index
83
- if self.function_call_output and len(self.function_call_output) > current_index:
84
- function_call = self.function_call_output[current_index]
85
- if function_call.type == "FUNCTION_CALL" and function_call.value is not None:
86
- return function_call.value.arguments or {}
87
- return {}
88
-
89
- def _extract_function_call_id(self) -> Optional[str]:
90
- """Extract function call ID from function call output."""
91
- current_index = getattr(self, "state").current_prompt_output_index
92
- if self.function_call_output and len(self.function_call_output) > current_index:
93
- function_call = self.function_call_output[current_index]
94
- if function_call.type == "FUNCTION_CALL" and function_call.value is not None:
95
- return function_call.value.id
96
- return None
97
-
98
79
  def _add_function_result_to_chat_history(self, result: Any, state: ToolCallingState) -> None:
99
80
  """Add function execution result to chat history."""
100
- function_call_id = self._extract_function_call_id()
81
+ function_call_id = self.function_call_id
101
82
  state.chat_history.append(
102
83
  ChatMessage(
103
84
  role="FUNCTION",
@@ -119,7 +100,14 @@ class ToolPromptNode(InlinePromptNode[ToolCallingState]):
119
100
  def run(self) -> Iterator[BaseOutput]:
120
101
  if self.max_prompt_iterations is not None and self.state.prompt_iterations >= self.max_prompt_iterations:
121
102
  max_iterations_message = f"Maximum number of prompt iterations `{self.max_prompt_iterations}` reached."
122
- raise NodeException(message=max_iterations_message, code=WorkflowErrorCode.NODE_EXECUTION)
103
+ raise NodeException(
104
+ message=max_iterations_message,
105
+ code=WorkflowErrorCode.NODE_EXECUTION,
106
+ raw_data={
107
+ "max_iterations": self.max_prompt_iterations,
108
+ "iterations_reached": self.state.prompt_iterations,
109
+ },
110
+ )
123
111
 
124
112
  generator = super().run()
125
113
  with self.state.__quiet__():
@@ -166,18 +154,9 @@ class DynamicSubworkflowDeploymentNode(SubworkflowDeploymentNode[ToolCallingStat
166
154
  """Node that executes a deployment definition with function call output."""
167
155
 
168
156
  def run(self) -> Iterator[BaseOutput]:
169
- arguments = self._extract_function_arguments()
170
-
171
157
  # Mypy doesn't like instance assignments of class attributes. It's safe in our case tho bc it's what
172
- # we do in the `__init__` method. Long term, instead of the function_call_output attribute above, we
173
- # want to do:
174
- # ```python
175
- # subworkflow_inputs = tool_prompt_node.Outputs.results[0]['value']['arguments'].if_(
176
- # tool_prompt_node.Outputs.results[0]['type'].equals('FUNCTION_CALL'),
177
- # {},
178
- # )
179
- # ```
180
- self.subworkflow_inputs = arguments # type:ignore[misc]
158
+ # we do in the `__init__` method.
159
+ self.subworkflow_inputs = self.arguments # type:ignore[misc]
181
160
 
182
161
  # Call the parent run method to execute the subworkflow
183
162
  outputs = {}
@@ -196,9 +175,18 @@ class DynamicInlineSubworkflowNode(
196
175
  """Node that executes an inline subworkflow with function call output."""
197
176
 
198
177
  def run(self) -> Iterator[BaseOutput]:
199
- arguments = self._extract_function_arguments()
178
+ # Merge arguments with resolved inputs from __vellum_inputs__
179
+ merged_inputs = self.arguments.copy()
180
+ vellum_inputs = getattr(self.subworkflow, "__vellum_inputs__", {})
181
+ if vellum_inputs:
182
+ for param_name, param_ref in vellum_inputs.items():
183
+ if isinstance(param_ref, BaseDescriptor):
184
+ resolved_value = param_ref.resolve(self.state)
185
+ else:
186
+ resolved_value = param_ref
187
+ merged_inputs[param_name] = resolved_value
200
188
 
201
- self.subworkflow_inputs = arguments # type: ignore[misc]
189
+ self.subworkflow_inputs = merged_inputs # type: ignore[misc]
202
190
 
203
191
  # Call the parent run method to execute the subworkflow with proper streaming
204
192
  outputs = {}
@@ -221,10 +209,8 @@ class FunctionNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
221
209
  result: Any
222
210
 
223
211
  def run(self) -> Iterator[BaseOutput]:
224
- arguments = self._extract_function_arguments()
225
-
226
212
  try:
227
- result = self.function_definition(**arguments)
213
+ result = self.function_definition(**self.arguments)
228
214
  except Exception as e:
229
215
  self._handle_tool_exception(e, "function", self.function_definition.__name__)
230
216
 
@@ -240,18 +226,15 @@ class ComposioNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
240
226
  composio_tool: ComposioToolDefinition
241
227
 
242
228
  def run(self) -> Iterator[BaseOutput]:
243
- # Extract arguments from function call
244
- arguments = self._extract_function_arguments()
245
-
246
229
  try:
247
230
  # Execute using ComposioService
248
231
  composio_service = ComposioService()
249
232
  if self.composio_tool.user_id is not None:
250
233
  result = composio_service.execute_tool(
251
- tool_name=self.composio_tool.action, arguments=arguments, user_id=self.composio_tool.user_id
234
+ tool_name=self.composio_tool.action, arguments=self.arguments, user_id=self.composio_tool.user_id
252
235
  )
253
236
  else:
254
- result = composio_service.execute_tool(tool_name=self.composio_tool.action, arguments=arguments)
237
+ result = composio_service.execute_tool(tool_name=self.composio_tool.action, arguments=self.arguments)
255
238
  except Exception as e:
256
239
  self._handle_tool_exception(e, "Composio tool", self.composio_tool.action)
257
240
 
@@ -270,11 +253,9 @@ class MCPNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
270
253
  result: Any
271
254
 
272
255
  def run(self) -> Iterator[BaseOutput]:
273
- arguments = self._extract_function_arguments()
274
-
275
256
  try:
276
257
  mcp_service = MCPService()
277
- result = mcp_service.execute_tool(tool_def=self.mcp_tool, arguments=arguments)
258
+ result = mcp_service.execute_tool(tool_def=self.mcp_tool, arguments=self.arguments)
278
259
  except Exception as e:
279
260
  self._handle_tool_exception(e, "MCP tool", self.mcp_tool.name)
280
261
 
@@ -293,7 +274,6 @@ class VellumIntegrationNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
293
274
  result: Any
294
275
 
295
276
  def run(self) -> Iterator[BaseOutput]:
296
- arguments = self._extract_function_arguments()
297
277
  vellum_client = self._context.vellum_client
298
278
 
299
279
  try:
@@ -302,7 +282,8 @@ class VellumIntegrationNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
302
282
  integration=self.vellum_integration_tool.integration_name,
303
283
  provider=self.vellum_integration_tool.provider.value,
304
284
  tool_name=self.vellum_integration_tool.name,
305
- arguments=arguments,
285
+ arguments=self.arguments,
286
+ toolkit_version=self.vellum_integration_tool.toolkit_version,
306
287
  )
307
288
  except NodeException as e:
308
289
  error_payload = {
@@ -315,7 +296,7 @@ class VellumIntegrationNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
315
296
  error_payload["error"]["raw_data"] = e.raw_data
316
297
 
317
298
  self._add_function_result_to_chat_history(error_payload, self.state)
318
- yield from []
299
+ yield BaseOutput(name="result", value=error_payload["error"])
319
300
  return
320
301
  except Exception as e:
321
302
  self._handle_tool_exception(e, "Vellum Integration tool", self.vellum_integration_tool.name)
@@ -348,7 +329,7 @@ class ElseNode(BaseNode[ToolCallingState]):
348
329
  def create_tool_prompt_node(
349
330
  ml_model: str,
350
331
  blocks: List[Union[PromptBlock, Dict[str, Any]]],
351
- functions: List[Tool],
332
+ functions: List[Union[ToolBase, MCPToolDefinition]],
352
333
  prompt_inputs: Optional[EntityInputsInterface],
353
334
  parameters: PromptParameters,
354
335
  max_prompt_iterations: Optional[int] = None,
@@ -357,7 +338,7 @@ def create_tool_prompt_node(
357
338
  settings: Optional[Union[PromptSettings, Dict[str, Any]]] = None,
358
339
  ) -> Type[ToolPromptNode]:
359
340
  if functions and len(functions) > 0:
360
- prompt_functions: List[Tool] = functions
341
+ prompt_functions: List[Union[ToolBase, MCPToolDefinition]] = functions
361
342
  else:
362
343
  prompt_functions = []
363
344
 
@@ -422,8 +403,28 @@ def create_tool_prompt_node(
422
403
  return node
423
404
 
424
405
 
406
+ def _create_function_call_expressions(
407
+ tool_prompt_node: Type[ToolPromptNode],
408
+ ) -> tuple[BaseDescriptor[dict], BaseDescriptor[Optional[str]]]:
409
+ """
410
+ Create expressions to extract arguments and function_call_id from tool_prompt_node outputs.
411
+
412
+ Returns:
413
+ A tuple of (arguments_expression, function_call_id_expression)
414
+
415
+ Note: These expressions assume the output at current_prompt_output_index is a valid FUNCTION_CALL.
416
+ The router node ensures this before routing to function nodes.
417
+ """
418
+ current_output = tool_prompt_node.Outputs.results[ToolCallingState.current_prompt_output_index]
419
+ # Extract arguments and function_call_id directly from the function call value
420
+ # Using coalesce to provide safe fallbacks if the structure is unexpected
421
+ arguments_expr: BaseDescriptor[dict] = current_output["value"]["arguments"].coalesce({})
422
+ function_call_id_expr: BaseDescriptor[Optional[str]] = current_output["value"]["id"].coalesce(None)
423
+ return arguments_expr, function_call_id_expr
424
+
425
+
425
426
  def create_router_node(
426
- functions: List[Tool],
427
+ functions: List[Union[ToolBase, MCPToolDefinition]],
427
428
  tool_prompt_node: Type[InlinePromptNode[ToolCallingState]],
428
429
  ) -> Type[RouterNode]:
429
430
  """Create a RouterNode with dynamic ports that route based on tool_prompt_node outputs."""
@@ -433,14 +434,7 @@ def create_router_node(
433
434
  Ports = type("Ports", (), {})
434
435
 
435
436
  # Collect all tool names
436
- tool_names: List[str] = []
437
- for function in functions:
438
- if isinstance(function, MCPServer):
439
- tool_functions: List[MCPToolDefinition] = compile_mcp_tool_definition(function)
440
- for tool_function in tool_functions:
441
- tool_names.append(get_mcp_tool_name(tool_function))
442
- else:
443
- tool_names.append(get_function_name(function))
437
+ tool_names: List[str] = [get_function_name(function) for function in functions]
444
438
 
445
439
  # Build conditions for each tool name
446
440
  conditions = [
@@ -484,7 +478,7 @@ def create_router_node(
484
478
 
485
479
 
486
480
  def create_function_node(
487
- function: ToolBase,
481
+ function: Union[ToolBase, MCPToolDefinition],
488
482
  tool_prompt_node: Type[ToolPromptNode],
489
483
  ) -> Type[BaseNode]:
490
484
  """
@@ -492,11 +486,17 @@ def create_function_node(
492
486
 
493
487
  For workflow functions: BaseNode
494
488
  For regular functions: BaseNode with direct function call
489
+ For MCP tools: MCPNode
495
490
 
496
491
  Args:
497
492
  function: The function to create a node for
498
493
  tool_prompt_node: The tool prompt node class
499
494
  """
495
+ if isinstance(function, MCPToolDefinition):
496
+ return create_mcp_tool_node(function, tool_prompt_node)
497
+
498
+ arguments_expr, function_call_id_expr = _create_function_call_expressions(tool_prompt_node)
499
+
500
500
  if isinstance(function, DeploymentDefinition):
501
501
  deployment = function.deployment_id or function.deployment_name
502
502
  release_tag = function.release_tag
@@ -507,7 +507,8 @@ def create_function_node(
507
507
  {
508
508
  "deployment": deployment,
509
509
  "release_tag": release_tag,
510
- "function_call_output": tool_prompt_node.Outputs.results,
510
+ "arguments": arguments_expr,
511
+ "function_call_id": function_call_id_expr,
511
512
  "__module__": __name__,
512
513
  },
513
514
  )
@@ -520,18 +521,26 @@ def create_function_node(
520
521
  (ComposioNode,),
521
522
  {
522
523
  "composio_tool": function,
523
- "function_call_output": tool_prompt_node.Outputs.results,
524
+ "arguments": arguments_expr,
525
+ "function_call_id": function_call_id_expr,
524
526
  "__module__": __name__,
525
527
  },
526
528
  )
527
529
  return node
528
530
  elif isinstance(function, VellumIntegrationToolDefinition):
531
+ display_class = type(
532
+ f"VellumIntegrationNodeDisplay_{function.name}",
533
+ (VellumIntegrationNode.Display,),
534
+ {"icon": "vellum:icon:plug", "color": "navy"},
535
+ )
529
536
  node = type(
530
537
  f"VellumIntegrationNode_{function.name}",
531
538
  (VellumIntegrationNode,),
532
539
  {
533
540
  "vellum_integration_tool": function,
534
- "function_call_output": tool_prompt_node.Outputs.results,
541
+ "arguments": arguments_expr,
542
+ "function_call_id": function_call_id_expr,
543
+ "Display": display_class,
535
544
  "__module__": __name__,
536
545
  },
537
546
  )
@@ -543,7 +552,8 @@ def create_function_node(
543
552
  (DynamicInlineSubworkflowNode,),
544
553
  {
545
554
  "subworkflow": function,
546
- "function_call_output": tool_prompt_node.Outputs.results,
555
+ "arguments": arguments_expr,
556
+ "function_call_id": function_call_id_expr,
547
557
  "__module__": __name__,
548
558
  },
549
559
  )
@@ -567,12 +577,19 @@ def create_function_node(
567
577
  wrapper.__name__ = func.__name__
568
578
  return wrapper
569
579
 
580
+ display_class = type(
581
+ f"FunctionNodeDisplay_{function.__name__}",
582
+ (FunctionNode.Display,),
583
+ {"icon": "vellum:icon:rectangle-code", "color": "purple"},
584
+ )
570
585
  node = type(
571
586
  f"FunctionNode_{function.__name__}",
572
587
  (FunctionNode,),
573
588
  {
574
589
  "function_definition": create_function_wrapper(function),
575
- "function_call_output": tool_prompt_node.Outputs.results,
590
+ "arguments": arguments_expr,
591
+ "function_call_id": function_call_id_expr,
592
+ "Display": display_class,
576
593
  "__module__": __name__,
577
594
  },
578
595
  )
@@ -584,12 +601,14 @@ def create_mcp_tool_node(
584
601
  tool_def: MCPToolDefinition,
585
602
  tool_prompt_node: Type[ToolPromptNode],
586
603
  ) -> Type[BaseNode]:
604
+ arguments_expr, function_call_id_expr = _create_function_call_expressions(tool_prompt_node)
587
605
  node = type(
588
606
  f"MCPNode_{tool_def.name}",
589
607
  (MCPNode,),
590
608
  {
591
609
  "mcp_tool": tool_def,
592
- "function_call_output": tool_prompt_node.Outputs.results,
610
+ "arguments": arguments_expr,
611
+ "function_call_id": function_call_id_expr,
593
612
  "__module__": __name__,
594
613
  },
595
614
  )
@@ -620,8 +639,10 @@ def create_else_node(
620
639
  return node
621
640
 
622
641
 
623
- def get_function_name(function: ToolBase) -> str:
624
- if isinstance(function, DeploymentDefinition):
642
+ def get_function_name(function: Union[ToolBase, MCPToolDefinition]) -> str:
643
+ if isinstance(function, MCPToolDefinition):
644
+ return get_mcp_tool_name(function)
645
+ elif isinstance(function, DeploymentDefinition):
625
646
  name = str(function.deployment_id or function.deployment_name)
626
647
  return name.replace("-", "")
627
648
  elif isinstance(function, ComposioToolDefinition):