griptape-nodes 0.52.1__py3-none-any.whl → 0.54.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. griptape_nodes/__init__.py +8 -942
  2. griptape_nodes/__main__.py +6 -0
  3. griptape_nodes/app/app.py +48 -86
  4. griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +35 -5
  5. griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +15 -1
  6. griptape_nodes/cli/__init__.py +1 -0
  7. griptape_nodes/cli/commands/__init__.py +1 -0
  8. griptape_nodes/cli/commands/config.py +74 -0
  9. griptape_nodes/cli/commands/engine.py +80 -0
  10. griptape_nodes/cli/commands/init.py +550 -0
  11. griptape_nodes/cli/commands/libraries.py +96 -0
  12. griptape_nodes/cli/commands/models.py +504 -0
  13. griptape_nodes/cli/commands/self.py +120 -0
  14. griptape_nodes/cli/main.py +56 -0
  15. griptape_nodes/cli/shared.py +75 -0
  16. griptape_nodes/common/__init__.py +1 -0
  17. griptape_nodes/common/directed_graph.py +71 -0
  18. griptape_nodes/drivers/storage/base_storage_driver.py +40 -20
  19. griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +24 -29
  20. griptape_nodes/drivers/storage/local_storage_driver.py +23 -14
  21. griptape_nodes/exe_types/core_types.py +60 -2
  22. griptape_nodes/exe_types/node_types.py +257 -38
  23. griptape_nodes/exe_types/param_components/__init__.py +1 -0
  24. griptape_nodes/exe_types/param_components/execution_status_component.py +138 -0
  25. griptape_nodes/machines/control_flow.py +195 -94
  26. griptape_nodes/machines/dag_builder.py +207 -0
  27. griptape_nodes/machines/fsm.py +10 -1
  28. griptape_nodes/machines/parallel_resolution.py +558 -0
  29. griptape_nodes/machines/{node_resolution.py → sequential_resolution.py} +30 -57
  30. griptape_nodes/node_library/library_registry.py +34 -1
  31. griptape_nodes/retained_mode/events/app_events.py +5 -1
  32. griptape_nodes/retained_mode/events/base_events.py +9 -9
  33. griptape_nodes/retained_mode/events/config_events.py +30 -0
  34. griptape_nodes/retained_mode/events/execution_events.py +2 -2
  35. griptape_nodes/retained_mode/events/model_events.py +296 -0
  36. griptape_nodes/retained_mode/events/node_events.py +4 -3
  37. griptape_nodes/retained_mode/griptape_nodes.py +34 -12
  38. griptape_nodes/retained_mode/managers/agent_manager.py +23 -5
  39. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +3 -1
  40. griptape_nodes/retained_mode/managers/config_manager.py +44 -3
  41. griptape_nodes/retained_mode/managers/context_manager.py +6 -5
  42. griptape_nodes/retained_mode/managers/event_manager.py +8 -2
  43. griptape_nodes/retained_mode/managers/flow_manager.py +150 -206
  44. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +1 -1
  45. griptape_nodes/retained_mode/managers/library_manager.py +35 -25
  46. griptape_nodes/retained_mode/managers/model_manager.py +1107 -0
  47. griptape_nodes/retained_mode/managers/node_manager.py +102 -220
  48. griptape_nodes/retained_mode/managers/object_manager.py +11 -5
  49. griptape_nodes/retained_mode/managers/os_manager.py +28 -13
  50. griptape_nodes/retained_mode/managers/secrets_manager.py +8 -4
  51. griptape_nodes/retained_mode/managers/settings.py +116 -7
  52. griptape_nodes/retained_mode/managers/static_files_manager.py +85 -12
  53. griptape_nodes/retained_mode/managers/sync_manager.py +17 -9
  54. griptape_nodes/retained_mode/managers/workflow_manager.py +186 -192
  55. griptape_nodes/retained_mode/retained_mode.py +19 -0
  56. griptape_nodes/servers/__init__.py +1 -0
  57. griptape_nodes/{mcp_server/server.py → servers/mcp.py} +1 -1
  58. griptape_nodes/{app/api.py → servers/static.py} +43 -40
  59. griptape_nodes/traits/add_param_button.py +1 -1
  60. griptape_nodes/traits/button.py +334 -6
  61. griptape_nodes/traits/color_picker.py +66 -0
  62. griptape_nodes/traits/multi_options.py +188 -0
  63. griptape_nodes/traits/numbers_selector.py +77 -0
  64. griptape_nodes/traits/options.py +93 -2
  65. griptape_nodes/traits/traits.json +4 -0
  66. griptape_nodes/utils/async_utils.py +31 -0
  67. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/METADATA +4 -1
  68. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/RECORD +71 -48
  69. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/WHEEL +1 -1
  70. /griptape_nodes/{mcp_server → servers}/ws_request_manager.py +0 -0
  71. {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/entry_points.txt +0 -0
@@ -37,6 +37,7 @@ from griptape_nodes.retained_mode.events.app_events import (
37
37
  )
38
38
  from griptape_nodes.retained_mode.events.base_events import (
39
39
  GriptapeNodeEvent,
40
+ ResultDetails,
40
41
  ResultPayloadFailure,
41
42
  )
42
43
  from griptape_nodes.retained_mode.events.flow_events import (
@@ -61,6 +62,7 @@ if TYPE_CHECKING:
61
62
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
62
63
  from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
63
64
  from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
65
+ from griptape_nodes.retained_mode.managers.model_manager import ModelManager
64
66
  from griptape_nodes.retained_mode.managers.node_manager import NodeManager
65
67
  from griptape_nodes.retained_mode.managers.object_manager import ObjectManager
66
68
  from griptape_nodes.retained_mode.managers.operation_manager import (
@@ -137,6 +139,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
137
139
  _flow_manager: FlowManager
138
140
  _context_manager: ContextManager
139
141
  _library_manager: LibraryManager
142
+ _model_manager: ModelManager
140
143
  _workflow_manager: WorkflowManager
141
144
  _workflow_variables_manager: VariablesManager
142
145
  _arbitrary_code_exec_manager: ArbitraryCodeExecManager
@@ -159,6 +162,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
159
162
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
160
163
  from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
161
164
  from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
165
+ from griptape_nodes.retained_mode.managers.model_manager import ModelManager
162
166
  from griptape_nodes.retained_mode.managers.node_manager import NodeManager
163
167
  from griptape_nodes.retained_mode.managers.object_manager import ObjectManager
164
168
  from griptape_nodes.retained_mode.managers.operation_manager import (
@@ -192,6 +196,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
192
196
  self._flow_manager = FlowManager(self._event_manager)
193
197
  self._context_manager = ContextManager(self._event_manager)
194
198
  self._library_manager = LibraryManager(self._event_manager)
199
+ self._model_manager = ModelManager(self._event_manager)
195
200
  self._workflow_manager = WorkflowManager(self._event_manager)
196
201
  self._workflow_variables_manager = VariablesManager(self._event_manager)
197
202
  self._arbitrary_code_exec_manager = ArbitraryCodeExecManager(self._event_manager)
@@ -252,7 +257,9 @@ class GriptapeNodes(metaclass=SingletonMeta):
252
257
  type(request).__name__,
253
258
  request,
254
259
  )
255
- return ResultPayloadFailure(exception=e)
260
+ return ResultPayloadFailure(
261
+ exception=e, result_details=f"Unhandled exception while processing {type(request).__name__}: {e}"
262
+ )
256
263
  else:
257
264
  return result_event.result
258
265
 
@@ -276,7 +283,9 @@ class GriptapeNodes(metaclass=SingletonMeta):
276
283
  type(request).__name__,
277
284
  request,
278
285
  )
279
- return ResultPayloadFailure(exception=e)
286
+ return ResultPayloadFailure(
287
+ exception=e, result_details=f"Unhandled exception while processing async {type(request).__name__}: {e}"
288
+ )
280
289
  else:
281
290
  return result_event.result
282
291
 
@@ -301,6 +310,10 @@ class GriptapeNodes(metaclass=SingletonMeta):
301
310
  def LibraryManager(cls) -> LibraryManager:
302
311
  return GriptapeNodes.get_instance()._library_manager
303
312
 
313
+ @classmethod
314
+ def ModelManager(cls) -> ModelManager:
315
+ return GriptapeNodes.get_instance()._model_manager
316
+
304
317
  @classmethod
305
318
  def ObjectManager(cls) -> ObjectManager:
306
319
  return GriptapeNodes.get_instance()._object_manager
@@ -421,6 +434,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
421
434
  major=engine_ver.major,
422
435
  minor=engine_ver.minor,
423
436
  patch=engine_ver.patch,
437
+ result_details="Engine version retrieved successfully.",
424
438
  )
425
439
  details = f"Attempted to get engine version. Failed because version string '{engine_ver}' wasn't in expected major.minor.patch format."
426
440
  logger.error(details)
@@ -447,7 +461,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
447
461
  await subscribe_to_topic(topic)
448
462
  logger.info("Subscribed to new session topic: %s", topic)
449
463
 
450
- return AppStartSessionResultSuccess(current_session_id)
464
+ return AppStartSessionResultSuccess(current_session_id, result_details="Session started successfully.")
451
465
 
452
466
  async def handle_session_end_request(self, _: AppEndSessionRequest) -> ResultPayload:
453
467
  from griptape_nodes.app.app import unsubscribe_from_topic
@@ -465,14 +479,19 @@ class GriptapeNodes(metaclass=SingletonMeta):
465
479
  unsubscribe_topic = f"sessions/{previous_session_id}/request"
466
480
  await unsubscribe_from_topic(unsubscribe_topic)
467
481
 
468
- return AppEndSessionResultSuccess(session_id=previous_session_id)
482
+ return AppEndSessionResultSuccess(
483
+ session_id=previous_session_id, result_details="Session ended successfully."
484
+ )
469
485
  except Exception as err:
470
486
  details = f"Failed to end session due to '{err}'."
471
487
  logger.error(details)
472
488
  return AppEndSessionResultFailure(result_details=details)
473
489
 
474
490
  def handle_get_session_request(self, _: AppGetSessionRequest) -> ResultPayload:
475
- return AppGetSessionResultSuccess(session_id=GriptapeNodes.SessionManager().get_active_session_id())
491
+ return AppGetSessionResultSuccess(
492
+ session_id=GriptapeNodes.SessionManager().get_active_session_id(),
493
+ result_details="Session ID retrieved successfully.",
494
+ )
476
495
 
477
496
  def handle_session_heartbeat_request(self, request: SessionHeartbeatRequest) -> ResultPayload: # noqa: ARG002
478
497
  """Handle session heartbeat requests.
@@ -487,8 +506,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
487
506
  return SessionHeartbeatResultFailure(result_details=details)
488
507
 
489
508
  details = f"Session heartbeat successful for session: {active_session_id}"
490
- logger.debug(details)
491
- return SessionHeartbeatResultSuccess()
509
+ return SessionHeartbeatResultSuccess(result_details=details)
492
510
  except Exception as err:
493
511
  details = f"Failed to handle session heartbeat: {err}"
494
512
  logger.error(details)
@@ -509,7 +527,6 @@ class GriptapeNodes(metaclass=SingletonMeta):
509
527
  # Get engine name
510
528
  engine_name = GriptapeNodes.EngineIdentityManager().get_engine_name()
511
529
 
512
- logger.debug("Engine heartbeat successful")
513
530
  return EngineHeartbeatResultSuccess(
514
531
  heartbeat_id=request.heartbeat_id,
515
532
  engine_version=engine_version,
@@ -517,6 +534,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
517
534
  engine_id=GriptapeNodes.EngineIdentityManager().get_active_engine_id(),
518
535
  session_id=GriptapeNodes.SessionManager().get_active_session_id(),
519
536
  timestamp=datetime.now(tz=UTC).isoformat(),
537
+ result_details="Engine heartbeat successful",
520
538
  **instance_info,
521
539
  **workflow_info,
522
540
  )
@@ -529,8 +547,9 @@ class GriptapeNodes(metaclass=SingletonMeta):
529
547
  """Handle requests to get the current engine name."""
530
548
  try:
531
549
  engine_name = GriptapeNodes.EngineIdentityManager().get_engine_name()
532
- logger.debug("Retrieved engine name: %s", engine_name)
533
- return GetEngineNameResultSuccess(engine_name=engine_name)
550
+ return GetEngineNameResultSuccess(
551
+ engine_name=engine_name, result_details="Engine name retrieved successfully."
552
+ )
534
553
  except Exception as err:
535
554
  error_message = f"Failed to get engine name: {err}"
536
555
  logger.error(error_message)
@@ -547,8 +566,11 @@ class GriptapeNodes(metaclass=SingletonMeta):
547
566
 
548
567
  # Set the new engine name
549
568
  GriptapeNodes.EngineIdentityManager().set_engine_name(request.engine_name.strip())
550
- logger.info("Engine name set to: %s", request.engine_name.strip())
551
- return SetEngineNameResultSuccess(engine_name=request.engine_name.strip())
569
+ details = f"Engine name set to: {request.engine_name.strip()}"
570
+ return SetEngineNameResultSuccess(
571
+ engine_name=request.engine_name.strip(),
572
+ result_details=ResultDetails(message=details, level=logging.INFO),
573
+ )
552
574
 
553
575
  except Exception as err:
554
576
  error_message = f"Failed to set engine name: {err}"
@@ -2,6 +2,7 @@ import asyncio
2
2
  import json
3
3
  import logging
4
4
  import os
5
+ import threading
5
6
  import uuid
6
7
  from typing import TYPE_CHECKING
7
8
 
@@ -37,6 +38,7 @@ from griptape_nodes.retained_mode.events.agent_events import (
37
38
  RunAgentResultStarted,
38
39
  RunAgentResultSuccess,
39
40
  )
41
+ from griptape_nodes.retained_mode.events.app_events import AppInitializationComplete
40
42
  from griptape_nodes.retained_mode.events.base_events import ExecutionEvent, ExecutionGriptapeNodeEvent, ResultPayload
41
43
  from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
42
44
  from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
@@ -45,6 +47,7 @@ from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
45
47
  from griptape_nodes.retained_mode.managers.static_files_manager import (
46
48
  StaticFilesManager,
47
49
  )
50
+ from griptape_nodes.servers.mcp import start_mcp_server
48
51
 
49
52
  if TYPE_CHECKING:
50
53
  from griptape.tools.mcp.sessions import StreamableHttpConnection
@@ -104,6 +107,11 @@ class AgentManager:
104
107
  event_manager.assign_manager_to_request_type(
105
108
  GetConversationMemoryRequest, self.on_handle_get_conversation_memory_request
106
109
  )
110
+ event_manager.add_listener_to_app_event(
111
+ AppInitializationComplete,
112
+ self.on_app_initialization_complete,
113
+ )
114
+ # TODO: Listen for shutdown event (https://github.com/griptape-ai/griptape-nodes/issues/2149) to stop mcp server
107
115
 
108
116
  def _initialize_prompt_driver(self) -> GriptapeCloudPromptDriver:
109
117
  api_key = secrets_manager.get_secret(API_KEY_ENV_VAR)
@@ -137,7 +145,7 @@ class AgentManager:
137
145
  if self.mcp_tool is None:
138
146
  self.mcp_tool = self._initialize_mcp_tool()
139
147
  await asyncio.to_thread(self._on_handle_run_agent_request, request)
140
- return RunAgentResultStarted()
148
+ return RunAgentResultStarted(result_details="Agent execution started successfully.")
141
149
 
142
150
  def _create_agent(self) -> Agent:
143
151
  output_schema = Schema(
@@ -207,7 +215,9 @@ class AgentManager:
207
215
  error=last_event.task_output.to_dict(), result_details=last_event.task_output.to_json()
208
216
  )
209
217
  if isinstance(last_event.task_output, JsonArtifact):
210
- return RunAgentResultSuccess(last_event.task_output.to_dict())
218
+ return RunAgentResultSuccess(
219
+ last_event.task_output.to_dict(), result_details="Agent execution completed successfully."
220
+ )
211
221
  err_msg = f"Unexpected final event: {last_event}"
212
222
  logger.error(err_msg)
213
223
  return RunAgentResultFailure(error=ErrorArtifact(last_event).to_dict(), result_details=err_msg)
@@ -226,7 +236,7 @@ class AgentManager:
226
236
  details = f"Error configuring agent: {e}"
227
237
  logger.error(details)
228
238
  return ConfigureAgentResultFailure(result_details=details)
229
- return ConfigureAgentResultSuccess()
239
+ return ConfigureAgentResultSuccess(result_details="Agent configured successfully.")
230
240
 
231
241
  def on_handle_reset_agent_conversation_memory_request(
232
242
  self, _: ResetAgentConversationMemoryRequest
@@ -237,7 +247,7 @@ class AgentManager:
237
247
  details = f"Error resetting agent conversation memory: {e}"
238
248
  logger.error(details)
239
249
  return ResetAgentConversationMemoryResultFailure(result_details=details)
240
- return ResetAgentConversationMemoryResultSuccess()
250
+ return ResetAgentConversationMemoryResultSuccess(result_details="Agent conversation memory reset successfully.")
241
251
 
242
252
  def on_handle_get_conversation_memory_request(self, _: GetConversationMemoryRequest) -> ResultPayload:
243
253
  try:
@@ -246,4 +256,12 @@ class AgentManager:
246
256
  details = f"Error getting conversation memory: {e}"
247
257
  logger.error(details)
248
258
  return GetConversationMemoryResultFailure(result_details=details)
249
- return GetConversationMemoryResultSuccess(runs=conversation_memory)
259
+ return GetConversationMemoryResultSuccess(
260
+ runs=conversation_memory, result_details="Conversation memory retrieved successfully."
261
+ )
262
+
263
+ def on_app_initialization_complete(self, _payload: AppInitializationComplete) -> None:
264
+ secrets_manager = GriptapeNodes.SecretsManager()
265
+ api_key = secrets_manager.get_secret("GT_CLOUD_API_KEY")
266
+ # Start MCP server in daemon thread
267
+ threading.Thread(target=start_mcp_server, args=(api_key,), daemon=True, name="mcp-server").start()
@@ -43,7 +43,9 @@ class ArbitraryCodeExecManager:
43
43
  python_output = exec(request.python_string) # noqa: S102
44
44
 
45
45
  captured_output = strip_ansi_codes(string_buffer.getvalue())
46
- result = RunArbitraryPythonStringResultSuccess(python_output=captured_output)
46
+ result = RunArbitraryPythonStringResultSuccess(
47
+ python_output=captured_output, result_details="Successfully executed Python string"
48
+ )
47
49
  except Exception as e:
48
50
  python_output = f"ERROR: {e}"
49
51
  result = RunArbitraryPythonStringResultFailure(python_output=python_output, result_details=python_output)
@@ -8,16 +8,18 @@ from typing import Any, Literal
8
8
  from pydantic import ValidationError
9
9
  from xdg_base_dirs import xdg_config_home
10
10
 
11
+ from griptape_nodes.node_library.library_registry import LibraryRegistry
11
12
  from griptape_nodes.retained_mode.events.app_events import AppInitializationComplete
12
- from griptape_nodes.retained_mode.events.base_events import (
13
- ResultPayload,
14
- )
13
+ from griptape_nodes.retained_mode.events.base_events import ResultPayload
15
14
  from griptape_nodes.retained_mode.events.config_events import (
16
15
  GetConfigCategoryRequest,
17
16
  GetConfigCategoryResultFailure,
18
17
  GetConfigCategoryResultSuccess,
19
18
  GetConfigPathRequest,
20
19
  GetConfigPathResultSuccess,
20
+ GetConfigSchemaRequest,
21
+ GetConfigSchemaResultFailure,
22
+ GetConfigSchemaResultSuccess,
21
23
  GetConfigValueRequest,
22
24
  GetConfigValueResultFailure,
23
25
  GetConfigValueResultSuccess,
@@ -84,6 +86,9 @@ class ConfigManager:
84
86
  event_manager.assign_manager_to_request_type(GetConfigValueRequest, self.on_handle_get_config_value_request)
85
87
  event_manager.assign_manager_to_request_type(SetConfigValueRequest, self.on_handle_set_config_value_request)
86
88
  event_manager.assign_manager_to_request_type(GetConfigPathRequest, self.on_handle_get_config_path_request)
89
+ event_manager.assign_manager_to_request_type(
90
+ GetConfigSchemaRequest, self.on_handle_get_config_schema_request
91
+ )
87
92
  event_manager.assign_manager_to_request_type(ResetConfigRequest, self.on_handle_reset_config_request)
88
93
 
89
94
  event_manager.add_listener_to_app_event(
@@ -194,6 +199,9 @@ class ConfigManager:
194
199
  merged_config = merge_dicts(merged_config, self.env_config)
195
200
  logger.debug("Merged config from environment variables: %s", list(self.env_config.keys()))
196
201
 
202
+ # Re-assign workspace path in case env var overrides it
203
+ self.workspace_path = merged_config["workspace_directory"]
204
+
197
205
  # Validate the full config against the Settings model.
198
206
  try:
199
207
  Settings.model_validate(merged_config)
@@ -412,6 +420,39 @@ class ConfigManager:
412
420
  result_details = "Successfully returned the config path."
413
421
  return GetConfigPathResultSuccess(config_path=str(USER_CONFIG_PATH), result_details=result_details)
414
422
 
423
+ def on_handle_get_config_schema_request(self, request: GetConfigSchemaRequest) -> ResultPayload: # noqa: ARG002
424
+ """Handle request to get the configuration schema with current values and library settings.
425
+
426
+ This method returns a clean structure with three main components:
427
+ 1. base_schema: Core settings schema from Pydantic Settings model with categories
428
+ 2. library_schemas: Library-specific schemas from definition files (preserves enums)
429
+ 3. current_values: All current configuration values from merged config
430
+
431
+ The approach separates concerns for frontend flexibility and simplicity.
432
+ Library settings with explicit schemas (including enums) are preserved, while
433
+ libraries without schemas get simple object types.
434
+ """
435
+ try:
436
+ # Get base settings schema and current values
437
+ base_schema = Settings.model_json_schema()
438
+ current_values = self.merged_config.copy()
439
+
440
+ # Get library schemas
441
+ library_schemas = LibraryRegistry.get_all_library_schemas()
442
+
443
+ # Return clean structure
444
+ schema_with_defaults = {
445
+ "base_schema": base_schema,
446
+ "library_schemas": library_schemas,
447
+ "current_values": current_values,
448
+ }
449
+
450
+ result_details = "Successfully returned the configuration schema with default values and library settings."
451
+ return GetConfigSchemaResultSuccess(schema=schema_with_defaults, result_details=result_details)
452
+ except Exception as e:
453
+ result_details = f"Failed to generate configuration schema: {e}"
454
+ return GetConfigSchemaResultFailure(result_details=result_details)
455
+
415
456
  def on_handle_reset_config_request(self, request: ResetConfigRequest) -> ResultPayload: # noqa: ARG002
416
457
  try:
417
458
  self.reset_user_config()
@@ -279,19 +279,20 @@ class ContextManager:
279
279
  # As of today, we only allow a single Workflow context at a time. This may change in the future.
280
280
  if self.has_current_workflow():
281
281
  msg = f"Attempted to set the Workflow '{request.workflow_name}' as the Current Context. Failed because an existing workflow, '{self.get_current_workflow_name()}', is already in the Current Context. In order to clear the existing workflow and remove all objects and references to it, issue a ClearAllObjectState request."
282
- logger.error(msg)
283
- return SetWorkflowContextFailure()
282
+ return SetWorkflowContextFailure(result_details=msg)
284
283
 
285
284
  self.push_workflow(request.workflow_name)
286
285
  msg = f"Successfully set the Workflow '{request.workflow_name}' as the Current Context."
287
- logger.debug(msg)
288
- return SetWorkflowContextSuccess()
286
+ return SetWorkflowContextSuccess(result_details=msg)
289
287
 
290
288
  def on_get_workflow_context_request(self, request: GetWorkflowContextRequest) -> ResultPayload: # noqa: ARG002
291
289
  workflow_name = None
292
290
  if self.has_current_workflow():
293
291
  workflow_name = self.get_current_workflow_name()
294
- return GetWorkflowContextSuccess(workflow_name=workflow_name)
292
+ return GetWorkflowContextSuccess(
293
+ workflow_name=workflow_name,
294
+ result_details=f"Successfully retrieved workflow context: {workflow_name or 'None'}",
295
+ )
295
296
 
296
297
  def workflow(self, workflow_name: str) -> ContextManager.WorkflowContext:
297
298
  """Create a context manager for a Workflow context.
@@ -294,5 +294,11 @@ class EventManager:
294
294
  app_event_type = type(app_event)
295
295
  if app_event_type in self._app_event_listeners:
296
296
  listener_set = self._app_event_listeners[app_event_type]
297
- for listener_callback in listener_set:
298
- await call_function(listener_callback, app_event)
297
+
298
+ await asyncio.gather(
299
+ *[
300
+ asyncio.create_task(call_function(listener_callback, app_event))
301
+ for listener_callback in listener_set
302
+ ],
303
+ return_exceptions=True,
304
+ )