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.
- griptape_nodes/__init__.py +8 -942
- griptape_nodes/__main__.py +6 -0
- griptape_nodes/app/app.py +48 -86
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +35 -5
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +15 -1
- griptape_nodes/cli/__init__.py +1 -0
- griptape_nodes/cli/commands/__init__.py +1 -0
- griptape_nodes/cli/commands/config.py +74 -0
- griptape_nodes/cli/commands/engine.py +80 -0
- griptape_nodes/cli/commands/init.py +550 -0
- griptape_nodes/cli/commands/libraries.py +96 -0
- griptape_nodes/cli/commands/models.py +504 -0
- griptape_nodes/cli/commands/self.py +120 -0
- griptape_nodes/cli/main.py +56 -0
- griptape_nodes/cli/shared.py +75 -0
- griptape_nodes/common/__init__.py +1 -0
- griptape_nodes/common/directed_graph.py +71 -0
- griptape_nodes/drivers/storage/base_storage_driver.py +40 -20
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +24 -29
- griptape_nodes/drivers/storage/local_storage_driver.py +23 -14
- griptape_nodes/exe_types/core_types.py +60 -2
- griptape_nodes/exe_types/node_types.py +257 -38
- griptape_nodes/exe_types/param_components/__init__.py +1 -0
- griptape_nodes/exe_types/param_components/execution_status_component.py +138 -0
- griptape_nodes/machines/control_flow.py +195 -94
- griptape_nodes/machines/dag_builder.py +207 -0
- griptape_nodes/machines/fsm.py +10 -1
- griptape_nodes/machines/parallel_resolution.py +558 -0
- griptape_nodes/machines/{node_resolution.py → sequential_resolution.py} +30 -57
- griptape_nodes/node_library/library_registry.py +34 -1
- griptape_nodes/retained_mode/events/app_events.py +5 -1
- griptape_nodes/retained_mode/events/base_events.py +9 -9
- griptape_nodes/retained_mode/events/config_events.py +30 -0
- griptape_nodes/retained_mode/events/execution_events.py +2 -2
- griptape_nodes/retained_mode/events/model_events.py +296 -0
- griptape_nodes/retained_mode/events/node_events.py +4 -3
- griptape_nodes/retained_mode/griptape_nodes.py +34 -12
- griptape_nodes/retained_mode/managers/agent_manager.py +23 -5
- griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +3 -1
- griptape_nodes/retained_mode/managers/config_manager.py +44 -3
- griptape_nodes/retained_mode/managers/context_manager.py +6 -5
- griptape_nodes/retained_mode/managers/event_manager.py +8 -2
- griptape_nodes/retained_mode/managers/flow_manager.py +150 -206
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +1 -1
- griptape_nodes/retained_mode/managers/library_manager.py +35 -25
- griptape_nodes/retained_mode/managers/model_manager.py +1107 -0
- griptape_nodes/retained_mode/managers/node_manager.py +102 -220
- griptape_nodes/retained_mode/managers/object_manager.py +11 -5
- griptape_nodes/retained_mode/managers/os_manager.py +28 -13
- griptape_nodes/retained_mode/managers/secrets_manager.py +8 -4
- griptape_nodes/retained_mode/managers/settings.py +116 -7
- griptape_nodes/retained_mode/managers/static_files_manager.py +85 -12
- griptape_nodes/retained_mode/managers/sync_manager.py +17 -9
- griptape_nodes/retained_mode/managers/workflow_manager.py +186 -192
- griptape_nodes/retained_mode/retained_mode.py +19 -0
- griptape_nodes/servers/__init__.py +1 -0
- griptape_nodes/{mcp_server/server.py → servers/mcp.py} +1 -1
- griptape_nodes/{app/api.py → servers/static.py} +43 -40
- griptape_nodes/traits/add_param_button.py +1 -1
- griptape_nodes/traits/button.py +334 -6
- griptape_nodes/traits/color_picker.py +66 -0
- griptape_nodes/traits/multi_options.py +188 -0
- griptape_nodes/traits/numbers_selector.py +77 -0
- griptape_nodes/traits/options.py +93 -2
- griptape_nodes/traits/traits.json +4 -0
- griptape_nodes/utils/async_utils.py +31 -0
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/METADATA +4 -1
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/RECORD +71 -48
- {griptape_nodes-0.52.1.dist-info → griptape_nodes-0.54.0.dist-info}/WHEEL +1 -1
- /griptape_nodes/{mcp_server → servers}/ws_request_manager.py +0 -0
- {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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
533
|
-
|
|
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
|
-
|
|
551
|
-
return SetEngineNameResultSuccess(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
298
|
-
|
|
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
|
+
)
|