griptape-nodes 0.53.0__py3-none-any.whl → 0.54.1__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 +5 -2
- griptape_nodes/app/app.py +4 -26
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +35 -5
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +15 -1
- griptape_nodes/cli/commands/config.py +4 -1
- griptape_nodes/cli/commands/init.py +5 -3
- griptape_nodes/cli/commands/libraries.py +14 -8
- griptape_nodes/cli/commands/models.py +504 -0
- griptape_nodes/cli/commands/self.py +5 -2
- griptape_nodes/cli/main.py +11 -1
- griptape_nodes/cli/shared.py +0 -9
- griptape_nodes/common/directed_graph.py +17 -1
- 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 +17 -13
- griptape_nodes/exe_types/node_types.py +219 -14
- 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 +129 -92
- griptape_nodes/machines/dag_builder.py +207 -0
- griptape_nodes/machines/parallel_resolution.py +264 -276
- griptape_nodes/machines/sequential_resolution.py +9 -7
- 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 +7 -7
- 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/griptape_nodes.py +10 -1
- griptape_nodes/retained_mode/managers/agent_manager.py +14 -0
- griptape_nodes/retained_mode/managers/config_manager.py +44 -3
- griptape_nodes/retained_mode/managers/event_manager.py +8 -2
- griptape_nodes/retained_mode/managers/flow_manager.py +45 -14
- griptape_nodes/retained_mode/managers/library_manager.py +3 -3
- griptape_nodes/retained_mode/managers/model_manager.py +1107 -0
- griptape_nodes/retained_mode/managers/node_manager.py +26 -26
- griptape_nodes/retained_mode/managers/object_manager.py +1 -1
- griptape_nodes/retained_mode/managers/os_manager.py +6 -6
- griptape_nodes/retained_mode/managers/settings.py +87 -9
- griptape_nodes/retained_mode/managers/static_files_manager.py +77 -9
- griptape_nodes/retained_mode/managers/sync_manager.py +10 -5
- griptape_nodes/retained_mode/managers/workflow_manager.py +101 -92
- 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/button.py +124 -6
- 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/utils/async_utils.py +31 -0
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/METADATA +3 -1
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/RECORD +56 -47
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/WHEEL +1 -1
- /griptape_nodes/{mcp_server → servers}/ws_request_manager.py +0 -0
- {griptape_nodes-0.53.0.dist-info → griptape_nodes-0.54.1.dist-info}/entry_points.txt +0 -0
|
@@ -63,12 +63,6 @@ class InitializeSpotlightState(State):
|
|
|
63
63
|
@staticmethod
|
|
64
64
|
async def on_enter(context: ResolutionContext) -> type[State] | None:
|
|
65
65
|
# If the focus stack is empty
|
|
66
|
-
current_node = context.current_node
|
|
67
|
-
GriptapeNodes.EventManager().put_event(
|
|
68
|
-
ExecutionGriptapeNodeEvent(
|
|
69
|
-
wrapped_event=ExecutionEvent(payload=CurrentDataNodeEvent(node_name=current_node.name))
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
66
|
if not context.paused:
|
|
73
67
|
return InitializeSpotlightState
|
|
74
68
|
return None
|
|
@@ -207,6 +201,11 @@ class ExecuteNodeState(State):
|
|
|
207
201
|
|
|
208
202
|
# Clear all of the current output values
|
|
209
203
|
# if node is locked, don't clear anything. skip all of this.
|
|
204
|
+
GriptapeNodes.EventManager().put_event(
|
|
205
|
+
ExecutionGriptapeNodeEvent(
|
|
206
|
+
wrapped_event=ExecutionEvent(payload=CurrentDataNodeEvent(node_name=current_node.name))
|
|
207
|
+
)
|
|
208
|
+
)
|
|
210
209
|
if current_node.lock:
|
|
211
210
|
return ExecuteNodeState
|
|
212
211
|
await ExecuteNodeState.collect_values_from_upstream_nodes(context)
|
|
@@ -375,7 +374,10 @@ class SequentialResolutionMachine(FSM[ResolutionContext]):
|
|
|
375
374
|
resolution_context = ResolutionContext()
|
|
376
375
|
super().__init__(resolution_context)
|
|
377
376
|
|
|
378
|
-
async def resolve_node(self, node: BaseNode) -> None:
|
|
377
|
+
async def resolve_node(self, node: BaseNode | None = None) -> None:
|
|
378
|
+
if node is None:
|
|
379
|
+
msg = "SequentialResolutionMachine requires a node to resolve"
|
|
380
|
+
raise ValueError(msg)
|
|
379
381
|
self._context.focus_stack.append(Focus(node=node))
|
|
380
382
|
await self.start(InitializeSpotlightState)
|
|
381
383
|
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple
|
|
5
5
|
|
|
6
|
-
from pydantic import BaseModel
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
7
|
|
|
8
8
|
from griptape_nodes.utils.metaclasses import SingletonMeta
|
|
9
9
|
|
|
@@ -85,6 +85,9 @@ class Setting(BaseModel):
|
|
|
85
85
|
category: str # Name of the category in the config
|
|
86
86
|
contents: dict[str, Any] # The actual settings content
|
|
87
87
|
description: str | None = None # Optional description for the setting
|
|
88
|
+
json_schema: dict[str, Any] | None = Field(
|
|
89
|
+
default=None, alias="schema"
|
|
90
|
+
) # JSON schema for the setting (including enums)
|
|
88
91
|
|
|
89
92
|
|
|
90
93
|
class LibrarySchema(BaseModel):
|
|
@@ -230,6 +233,36 @@ class LibraryRegistry(metaclass=SingletonMeta):
|
|
|
230
233
|
# Ask the library to create the node.
|
|
231
234
|
return dest_library.create_node(node_type=node_type, name=name, metadata=metadata)
|
|
232
235
|
|
|
236
|
+
@classmethod
|
|
237
|
+
def get_all_library_schemas(cls) -> dict[str, dict]:
|
|
238
|
+
"""Get schemas from all loaded libraries.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Dictionary mapping category names to their JSON Schema dicts
|
|
242
|
+
"""
|
|
243
|
+
instance = cls()
|
|
244
|
+
schemas = {}
|
|
245
|
+
|
|
246
|
+
# Get explicit schemas from loaded libraries
|
|
247
|
+
for library in instance._libraries.values():
|
|
248
|
+
library_data = library.get_library_data()
|
|
249
|
+
if library_data.settings:
|
|
250
|
+
for setting in library_data.settings:
|
|
251
|
+
if setting.json_schema:
|
|
252
|
+
schemas[setting.category] = {
|
|
253
|
+
"type": "object",
|
|
254
|
+
"properties": setting.json_schema,
|
|
255
|
+
"title": setting.description or f"{setting.category.title()} Settings",
|
|
256
|
+
}
|
|
257
|
+
else:
|
|
258
|
+
# Create fallback schema for settings without explicit schemas
|
|
259
|
+
schemas[setting.category] = {
|
|
260
|
+
"type": "object",
|
|
261
|
+
"title": setting.description or f"{setting.category.title()} Settings",
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return schemas
|
|
265
|
+
|
|
233
266
|
|
|
234
267
|
class Library:
|
|
235
268
|
"""A collection of nodes curated by library author.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
|
|
3
3
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
4
4
|
AppPayload,
|
|
@@ -76,6 +76,10 @@ class AppGetSessionResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
|
76
76
|
class AppInitializationComplete(AppPayload):
|
|
77
77
|
"""Application initialization completed successfully. All subsystems ready."""
|
|
78
78
|
|
|
79
|
+
libraries_to_register: list[str] = field(default_factory=list)
|
|
80
|
+
workflows_to_register: list[str] = field(default_factory=list)
|
|
81
|
+
models_to_download: list[str] = field(default_factory=list)
|
|
82
|
+
|
|
79
83
|
|
|
80
84
|
@dataclass
|
|
81
85
|
@PayloadRegistry.register
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
import logging
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from dataclasses import asdict, dataclass, field, is_dataclass
|
|
7
|
-
from typing import TYPE_CHECKING, Any, ClassVar,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
|
|
8
8
|
|
|
9
9
|
from griptape.artifacts import BaseArtifact
|
|
10
10
|
from griptape.mixins.serializable_mixin import SerializableMixin
|
|
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
|
|
|
20
20
|
class ResultDetail:
|
|
21
21
|
"""A single detail about an operation result, including logging level and human readable message."""
|
|
22
22
|
|
|
23
|
-
level:
|
|
23
|
+
level: int
|
|
24
24
|
message: str
|
|
25
25
|
|
|
26
26
|
|
|
@@ -34,7 +34,7 @@ class ResultDetails:
|
|
|
34
34
|
self,
|
|
35
35
|
*result_details: ResultDetail,
|
|
36
36
|
message: str | None = None,
|
|
37
|
-
level:
|
|
37
|
+
level: int | None = None,
|
|
38
38
|
logger: logging.Logger | str | None = "griptape_nodes",
|
|
39
39
|
):
|
|
40
40
|
"""Initialize with ResultDetail objects or create a single one from message/level.
|
|
@@ -67,8 +67,8 @@ class ResultDetails:
|
|
|
67
67
|
logger = logging.getLogger(logger)
|
|
68
68
|
|
|
69
69
|
for detail in self.result_details:
|
|
70
|
-
|
|
71
|
-
logger.log(
|
|
70
|
+
# Handle both string and int levels
|
|
71
|
+
logger.log(detail.level, detail.message)
|
|
72
72
|
except Exception: # noqa: S110
|
|
73
73
|
# If logging fails for any reason, don't let it break the ResultDetails creation
|
|
74
74
|
pass
|
|
@@ -140,7 +140,7 @@ class ResultPayloadSuccess(ResultPayload, ABC):
|
|
|
140
140
|
def __post_init__(self) -> None:
|
|
141
141
|
"""Initialize success result with INFO level default for strings."""
|
|
142
142
|
if isinstance(self.result_details, str):
|
|
143
|
-
self.result_details = ResultDetails(message=self.result_details, level=
|
|
143
|
+
self.result_details = ResultDetails(message=self.result_details, level=logging.DEBUG)
|
|
144
144
|
|
|
145
145
|
def succeeded(self) -> bool:
|
|
146
146
|
"""Returns True as this is a success result.
|
|
@@ -162,7 +162,7 @@ class ResultPayloadFailure(ResultPayload, ABC):
|
|
|
162
162
|
def __post_init__(self) -> None:
|
|
163
163
|
"""Initialize failure result with ERROR level default for strings."""
|
|
164
164
|
if isinstance(self.result_details, str):
|
|
165
|
-
self.result_details = ResultDetails(message=self.result_details, level=
|
|
165
|
+
self.result_details = ResultDetails(message=self.result_details, level=logging.ERROR)
|
|
166
166
|
|
|
167
167
|
def succeeded(self) -> bool:
|
|
168
168
|
"""Returns False as this is a failure result.
|
|
@@ -194,3 +194,33 @@ class ResetConfigResultSuccess(ResultPayloadSuccess):
|
|
|
194
194
|
@PayloadRegistry.register
|
|
195
195
|
class ResetConfigResultFailure(ResultPayloadFailure):
|
|
196
196
|
"""Configuration reset failed. Common causes: file system errors, permission issues, initialization errors."""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass
|
|
200
|
+
@PayloadRegistry.register
|
|
201
|
+
class GetConfigSchemaRequest(RequestPayload):
|
|
202
|
+
"""Get the JSON schema for the configuration model.
|
|
203
|
+
|
|
204
|
+
Use when: Frontend needs to understand field types, enums, and validation rules
|
|
205
|
+
for rendering appropriate UI components (dropdowns, text inputs, etc.).
|
|
206
|
+
|
|
207
|
+
Results: GetConfigSchemaResultSuccess (with schema) | GetConfigSchemaResultFailure (schema generation error)
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@dataclass
|
|
212
|
+
@PayloadRegistry.register
|
|
213
|
+
class GetConfigSchemaResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
214
|
+
"""Configuration schema retrieved successfully.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
schema: The JSON schema for the configuration model
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
schema: dict[str, Any]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass
|
|
224
|
+
@PayloadRegistry.register
|
|
225
|
+
class GetConfigSchemaResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
226
|
+
"""Configuration schema retrieval failed. Common causes: schema generation error, model validation issues."""
|
|
@@ -227,8 +227,8 @@ class GetFlowStateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
|
227
227
|
resolving_node: Name of the node currently being resolved (if any)
|
|
228
228
|
"""
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
resolving_node: str | None
|
|
230
|
+
control_nodes: list[str] | None
|
|
231
|
+
resolving_node: list[str] | None
|
|
232
232
|
|
|
233
233
|
|
|
234
234
|
@dataclass
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from griptape_nodes.retained_mode.events.base_events import (
|
|
4
|
+
RequestPayload,
|
|
5
|
+
ResultPayloadFailure,
|
|
6
|
+
ResultPayloadSuccess,
|
|
7
|
+
WorkflowNotAlteredMixin,
|
|
8
|
+
)
|
|
9
|
+
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
@PayloadRegistry.register
|
|
14
|
+
class DownloadModelRequest(RequestPayload):
|
|
15
|
+
"""Download a model from Hugging Face Hub.
|
|
16
|
+
|
|
17
|
+
Use when: Downloading models for local inference, caching models for offline use,
|
|
18
|
+
retrieving specific model versions or files from Hugging Face repositories.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
model_id: Model identifier (e.g., "microsoft/DialoGPT-medium") or full URL to Hugging Face model
|
|
22
|
+
local_dir: Optional local directory to download the model to (defaults to Hugging Face cache)
|
|
23
|
+
repo_type: Type of repository ("model", "dataset", or "space"). Defaults to "model"
|
|
24
|
+
revision: Git revision (branch, tag, or commit hash) to download. Defaults to "main"
|
|
25
|
+
allow_patterns: List of glob patterns to include when downloading. None means all files
|
|
26
|
+
ignore_patterns: List of glob patterns to exclude when downloading
|
|
27
|
+
|
|
28
|
+
Results: DownloadModelResultSuccess (with local_path) | DownloadModelResultFailure (download error)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
model_id: str
|
|
32
|
+
local_dir: str | None = None
|
|
33
|
+
repo_type: str = "model"
|
|
34
|
+
revision: str = "main"
|
|
35
|
+
allow_patterns: list[str] | None = None
|
|
36
|
+
ignore_patterns: list[str] | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
@PayloadRegistry.register
|
|
41
|
+
class DownloadModelResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
42
|
+
"""Model download completed successfully.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
model_id: The model ID that was downloaded
|
|
46
|
+
repo_info: Additional repository information returned from the download
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
model_id: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
@PayloadRegistry.register
|
|
54
|
+
class DownloadModelResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
55
|
+
"""Model download failed. Common causes: invalid model ID, network error, authentication required, storage full."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class ModelInfo:
|
|
60
|
+
"""Information about a model."""
|
|
61
|
+
|
|
62
|
+
model_id: str
|
|
63
|
+
local_path: str | None = None
|
|
64
|
+
size_bytes: int | None = None
|
|
65
|
+
author: str | None = None
|
|
66
|
+
downloads: int | None = None
|
|
67
|
+
likes: int | None = None
|
|
68
|
+
created_at: str | None = None
|
|
69
|
+
updated_at: str | None = None
|
|
70
|
+
task: str | None = None
|
|
71
|
+
library: str | None = None
|
|
72
|
+
tags: list[str] | None = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class QueryInfo:
|
|
77
|
+
"""Information about a search query."""
|
|
78
|
+
|
|
79
|
+
query: str | None = None
|
|
80
|
+
task: str | None = None
|
|
81
|
+
library: str | None = None
|
|
82
|
+
author: str | None = None
|
|
83
|
+
tags: list[str] | None = None
|
|
84
|
+
limit: int = 20
|
|
85
|
+
sort: str = "downloads"
|
|
86
|
+
direction: str = "desc"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
@PayloadRegistry.register
|
|
91
|
+
class ListModelsRequest(RequestPayload):
|
|
92
|
+
"""List all downloaded models from the local cache.
|
|
93
|
+
|
|
94
|
+
Use when: Viewing what models are available locally, checking cache usage,
|
|
95
|
+
managing local model storage.
|
|
96
|
+
|
|
97
|
+
Results: ListModelsResultSuccess (with model list) | ListModelsResultFailure (listing error)
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
@PayloadRegistry.register
|
|
103
|
+
class ListModelsResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
104
|
+
"""Model listing completed successfully.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
models: List of model information containing model_id, local_path, size_bytes, etc.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
models: list[ModelInfo]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass
|
|
114
|
+
@PayloadRegistry.register
|
|
115
|
+
class ListModelsResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
116
|
+
"""Model listing failed. Common causes: cache directory access error, filesystem error."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
@PayloadRegistry.register
|
|
121
|
+
class DeleteModelRequest(RequestPayload):
|
|
122
|
+
"""Delete a downloaded model from the local cache.
|
|
123
|
+
|
|
124
|
+
Use when: Cleaning up disk space, removing unused models, managing local storage.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
model_id: Model identifier to delete from local cache
|
|
128
|
+
|
|
129
|
+
Results: DeleteModelResultSuccess (deletion confirmed) | DeleteModelResultFailure (deletion error)
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
model_id: str
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass
|
|
136
|
+
@PayloadRegistry.register
|
|
137
|
+
class DeleteModelResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
138
|
+
"""Model deletion completed successfully.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
model_id: The model ID that was deleted
|
|
142
|
+
deleted_path: Local path that was removed
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
model_id: str
|
|
146
|
+
deleted_path: str
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass
|
|
150
|
+
@PayloadRegistry.register
|
|
151
|
+
class DeleteModelResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
152
|
+
"""Model deletion failed. Common causes: model not found, filesystem error, permission denied."""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass
|
|
156
|
+
@PayloadRegistry.register
|
|
157
|
+
class ListModelDownloadsRequest(RequestPayload):
|
|
158
|
+
"""List download status for a specific model or all downloads.
|
|
159
|
+
|
|
160
|
+
Use when: Checking progress of ongoing downloads, viewing download history,
|
|
161
|
+
monitoring download completion.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
model_id: Optional model identifier to get status for. If None, returns all downloads.
|
|
165
|
+
|
|
166
|
+
Results: ListModelDownloadsResultSuccess (with status data) | ListModelDownloadsResultFailure (query error)
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
model_id: str | None = None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass
|
|
173
|
+
class ModelDownloadStatus:
|
|
174
|
+
"""Model download status tracking multiple files."""
|
|
175
|
+
|
|
176
|
+
model_id: str
|
|
177
|
+
status: str # "downloading", "completed", "failed"
|
|
178
|
+
started_at: str
|
|
179
|
+
updated_at: str
|
|
180
|
+
total_files: int | None = None
|
|
181
|
+
completed_files: int | None = None
|
|
182
|
+
failed_files: int | None = None
|
|
183
|
+
# Optional fields for completed downloads
|
|
184
|
+
completed_at: str | None = None
|
|
185
|
+
local_path: str | None = None
|
|
186
|
+
# Optional fields for failed downloads
|
|
187
|
+
failed_at: str | None = None
|
|
188
|
+
error_message: str | None = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dataclass
|
|
192
|
+
@PayloadRegistry.register
|
|
193
|
+
class ListModelDownloadsResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
194
|
+
"""Model download status retrieved successfully.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
downloads: List of download status records or single status if model_id was specified
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
downloads: list[ModelDownloadStatus]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@dataclass
|
|
204
|
+
@PayloadRegistry.register
|
|
205
|
+
class ListModelDownloadsResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
206
|
+
"""Model download status query failed. Common causes: filesystem error, invalid model ID."""
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@dataclass
|
|
210
|
+
@PayloadRegistry.register
|
|
211
|
+
class DeleteModelDownloadRequest(RequestPayload):
|
|
212
|
+
"""Delete download status tracking records for a model.
|
|
213
|
+
|
|
214
|
+
Use when: Cleaning up orphaned download status files, removing tracking data
|
|
215
|
+
for models that are no longer needed.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
model_id: Model identifier to remove download status for
|
|
219
|
+
|
|
220
|
+
Results: DeleteModelDownloadResultSuccess (deletion confirmed) | DeleteModelDownloadResultFailure (deletion error)
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
model_id: str
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass
|
|
227
|
+
@PayloadRegistry.register
|
|
228
|
+
class DeleteModelDownloadResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
229
|
+
"""Model download status deletion completed successfully.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
model_id: The model ID whose download status was deleted
|
|
233
|
+
deleted_path: Path to the status file that was removed
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
model_id: str
|
|
237
|
+
deleted_path: str
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@dataclass
|
|
241
|
+
@PayloadRegistry.register
|
|
242
|
+
class DeleteModelDownloadResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
243
|
+
"""Model download status deletion failed. Common causes: status not found, filesystem error, permission denied."""
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass
|
|
247
|
+
@PayloadRegistry.register
|
|
248
|
+
class SearchModelsRequest(RequestPayload):
|
|
249
|
+
"""Search for models on Hugging Face Hub.
|
|
250
|
+
|
|
251
|
+
Use when: Finding models by name, filtering models by task or library,
|
|
252
|
+
discovering available models for specific use cases.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
query: Search query string to match against model names and descriptions
|
|
256
|
+
task: Filter by task type (e.g., "text-generation", "image-classification")
|
|
257
|
+
library: Filter by library (e.g., "transformers", "diffusers", "timm")
|
|
258
|
+
author: Filter by author/organization name
|
|
259
|
+
tags: List of tags to filter by
|
|
260
|
+
limit: Maximum number of results to return (default: 20, max: 100)
|
|
261
|
+
sort: Sort results by "downloads", "likes", "updated", or "created" (default: "downloads")
|
|
262
|
+
direction: Sort direction "asc" or "desc" (default: "desc")
|
|
263
|
+
|
|
264
|
+
Results: SearchModelsResultSuccess (with model list) | SearchModelsResultFailure (search error)
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
query: str | None = None
|
|
268
|
+
task: str | None = None
|
|
269
|
+
library: str | None = None
|
|
270
|
+
author: str | None = None
|
|
271
|
+
tags: list[str] | None = None
|
|
272
|
+
limit: int = 20
|
|
273
|
+
sort: str = "downloads"
|
|
274
|
+
direction: str = "desc"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@dataclass
|
|
278
|
+
@PayloadRegistry.register
|
|
279
|
+
class SearchModelsResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
280
|
+
"""Model search completed successfully.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
models: List of model information containing id, author, downloads, etc.
|
|
284
|
+
total_results: Total number of models matching the search criteria
|
|
285
|
+
query_info: Information about the search query parameters used
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
models: list[ModelInfo]
|
|
289
|
+
total_results: int
|
|
290
|
+
query_info: QueryInfo
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@dataclass
|
|
294
|
+
@PayloadRegistry.register
|
|
295
|
+
class SearchModelsResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
296
|
+
"""Model search failed. Common causes: network error, invalid parameters, API limits."""
|
|
@@ -62,6 +62,7 @@ if TYPE_CHECKING:
|
|
|
62
62
|
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
63
63
|
from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
|
|
64
64
|
from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
|
|
65
|
+
from griptape_nodes.retained_mode.managers.model_manager import ModelManager
|
|
65
66
|
from griptape_nodes.retained_mode.managers.node_manager import NodeManager
|
|
66
67
|
from griptape_nodes.retained_mode.managers.object_manager import ObjectManager
|
|
67
68
|
from griptape_nodes.retained_mode.managers.operation_manager import (
|
|
@@ -138,6 +139,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
138
139
|
_flow_manager: FlowManager
|
|
139
140
|
_context_manager: ContextManager
|
|
140
141
|
_library_manager: LibraryManager
|
|
142
|
+
_model_manager: ModelManager
|
|
141
143
|
_workflow_manager: WorkflowManager
|
|
142
144
|
_workflow_variables_manager: VariablesManager
|
|
143
145
|
_arbitrary_code_exec_manager: ArbitraryCodeExecManager
|
|
@@ -160,6 +162,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
160
162
|
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
161
163
|
from griptape_nodes.retained_mode.managers.flow_manager import FlowManager
|
|
162
164
|
from griptape_nodes.retained_mode.managers.library_manager import LibraryManager
|
|
165
|
+
from griptape_nodes.retained_mode.managers.model_manager import ModelManager
|
|
163
166
|
from griptape_nodes.retained_mode.managers.node_manager import NodeManager
|
|
164
167
|
from griptape_nodes.retained_mode.managers.object_manager import ObjectManager
|
|
165
168
|
from griptape_nodes.retained_mode.managers.operation_manager import (
|
|
@@ -193,6 +196,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
193
196
|
self._flow_manager = FlowManager(self._event_manager)
|
|
194
197
|
self._context_manager = ContextManager(self._event_manager)
|
|
195
198
|
self._library_manager = LibraryManager(self._event_manager)
|
|
199
|
+
self._model_manager = ModelManager(self._event_manager)
|
|
196
200
|
self._workflow_manager = WorkflowManager(self._event_manager)
|
|
197
201
|
self._workflow_variables_manager = VariablesManager(self._event_manager)
|
|
198
202
|
self._arbitrary_code_exec_manager = ArbitraryCodeExecManager(self._event_manager)
|
|
@@ -306,6 +310,10 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
306
310
|
def LibraryManager(cls) -> LibraryManager:
|
|
307
311
|
return GriptapeNodes.get_instance()._library_manager
|
|
308
312
|
|
|
313
|
+
@classmethod
|
|
314
|
+
def ModelManager(cls) -> ModelManager:
|
|
315
|
+
return GriptapeNodes.get_instance()._model_manager
|
|
316
|
+
|
|
309
317
|
@classmethod
|
|
310
318
|
def ObjectManager(cls) -> ObjectManager:
|
|
311
319
|
return GriptapeNodes.get_instance()._object_manager
|
|
@@ -560,7 +568,8 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
560
568
|
GriptapeNodes.EngineIdentityManager().set_engine_name(request.engine_name.strip())
|
|
561
569
|
details = f"Engine name set to: {request.engine_name.strip()}"
|
|
562
570
|
return SetEngineNameResultSuccess(
|
|
563
|
-
engine_name=request.engine_name.strip(),
|
|
571
|
+
engine_name=request.engine_name.strip(),
|
|
572
|
+
result_details=ResultDetails(message=details, level=logging.INFO),
|
|
564
573
|
)
|
|
565
574
|
|
|
566
575
|
except Exception as 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)
|
|
@@ -251,3 +259,9 @@ class AgentManager:
|
|
|
251
259
|
return GetConversationMemoryResultSuccess(
|
|
252
260
|
runs=conversation_memory, result_details="Conversation memory retrieved successfully."
|
|
253
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()
|
|
@@ -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()
|