griptape-nodes 0.45.1__py3-none-any.whl → 0.47.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 +51 -14
- griptape_nodes/exe_types/core_types.py +65 -10
- griptape_nodes/exe_types/node_types.py +10 -0
- griptape_nodes/machines/node_resolution.py +10 -8
- griptape_nodes/node_library/workflow_registry.py +1 -1
- griptape_nodes/retained_mode/events/base_events.py +74 -1
- griptape_nodes/retained_mode/events/secrets_events.py +2 -0
- griptape_nodes/retained_mode/events/workflow_events.py +4 -2
- griptape_nodes/retained_mode/griptape_nodes.py +17 -13
- griptape_nodes/retained_mode/managers/agent_manager.py +8 -6
- griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +1 -1
- griptape_nodes/retained_mode/managers/config_manager.py +36 -45
- griptape_nodes/retained_mode/managers/flow_manager.py +98 -98
- griptape_nodes/retained_mode/managers/library_manager.py +51 -51
- griptape_nodes/retained_mode/managers/node_manager.py +122 -129
- griptape_nodes/retained_mode/managers/object_manager.py +9 -10
- griptape_nodes/retained_mode/managers/os_manager.py +31 -31
- griptape_nodes/retained_mode/managers/secrets_manager.py +5 -5
- griptape_nodes/retained_mode/managers/static_files_manager.py +18 -17
- griptape_nodes/retained_mode/managers/sync_manager.py +3 -2
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +84 -1
- griptape_nodes/retained_mode/managers/workflow_manager.py +221 -163
- griptape_nodes/retained_mode/retained_mode.py +22 -44
- griptape_nodes/version_compatibility/workflow_versions/__init__.py +1 -0
- griptape_nodes/version_compatibility/workflow_versions/v0_7_0/__init__.py +1 -0
- griptape_nodes/version_compatibility/workflow_versions/v0_7_0/local_executor_argument_addition.py +42 -0
- {griptape_nodes-0.45.1.dist-info → griptape_nodes-0.47.0.dist-info}/METADATA +1 -1
- {griptape_nodes-0.45.1.dist-info → griptape_nodes-0.47.0.dist-info}/RECORD +30 -27
- {griptape_nodes-0.45.1.dist-info → griptape_nodes-0.47.0.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.45.1.dist-info → griptape_nodes-0.47.0.dist-info}/entry_points.txt +0 -0
griptape_nodes/__init__.py
CHANGED
|
@@ -154,21 +154,23 @@ def _handle_storage_backend_config(config: InitConfig) -> str | None:
|
|
|
154
154
|
return storage_backend
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def _handle_bucket_config(config: InitConfig
|
|
158
|
-
"""Handle bucket configuration step (depends on API key
|
|
159
|
-
|
|
157
|
+
def _handle_bucket_config(config: InitConfig) -> str | None:
|
|
158
|
+
"""Handle bucket configuration step (depends on API key)."""
|
|
159
|
+
bucket_id = None
|
|
160
160
|
|
|
161
|
-
if
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
if config.interactive:
|
|
162
|
+
# First ask if they want to configure a bucket
|
|
163
|
+
configure_bucket = _prompt_for_bucket_configuration()
|
|
164
|
+
if configure_bucket:
|
|
165
|
+
bucket_id = _prompt_for_gtc_bucket_name(default_bucket_name=config.bucket_name)
|
|
166
|
+
elif config.bucket_name is not None:
|
|
167
|
+
bucket_id = _get_or_create_bucket_id(config.bucket_name)
|
|
166
168
|
|
|
167
|
-
if
|
|
168
|
-
secrets_manager.set_secret("GT_CLOUD_BUCKET_ID",
|
|
169
|
-
console.print(f"[bold green]
|
|
169
|
+
if bucket_id is not None:
|
|
170
|
+
secrets_manager.set_secret("GT_CLOUD_BUCKET_ID", bucket_id)
|
|
171
|
+
console.print(f"[bold green]Bucket ID set to: {bucket_id}[/bold green]")
|
|
170
172
|
|
|
171
|
-
return
|
|
173
|
+
return bucket_id
|
|
172
174
|
|
|
173
175
|
|
|
174
176
|
def _handle_advanced_library_config(config: InitConfig) -> bool | None:
|
|
@@ -211,9 +213,9 @@ def _run_init_configuration(config: InitConfig) -> None:
|
|
|
211
213
|
|
|
212
214
|
_handle_workspace_config(config)
|
|
213
215
|
|
|
214
|
-
|
|
216
|
+
_handle_storage_backend_config(config)
|
|
215
217
|
|
|
216
|
-
_handle_bucket_config(config
|
|
218
|
+
_handle_bucket_config(config)
|
|
217
219
|
|
|
218
220
|
_handle_advanced_library_config(config)
|
|
219
221
|
|
|
@@ -477,6 +479,41 @@ def _get_griptape_cloud_buckets_and_display_table() -> tuple[list[str], dict[str
|
|
|
477
479
|
return bucket_names, name_to_id, table
|
|
478
480
|
|
|
479
481
|
|
|
482
|
+
def _prompt_for_bucket_configuration() -> bool:
|
|
483
|
+
"""Prompts the user whether to configure a bucket for multi-machine workflow and asset syncing."""
|
|
484
|
+
# Check if there's already a bucket configured
|
|
485
|
+
current_bucket_id = secrets_manager.get_secret("GT_CLOUD_BUCKET_ID", should_error_on_not_found=False)
|
|
486
|
+
|
|
487
|
+
if current_bucket_id:
|
|
488
|
+
explainer = f"""[bold cyan]Griptape Cloud Bucket Configuration[/bold cyan]
|
|
489
|
+
You currently have a bucket configured (ID: {current_bucket_id}).
|
|
490
|
+
|
|
491
|
+
Buckets are used for multi-machine workflow and asset syncing, allowing you to:
|
|
492
|
+
- Share workflows and assets across multiple devices
|
|
493
|
+
- Sync generated content between different Griptape Nodes instances
|
|
494
|
+
- Access your work from anywhere
|
|
495
|
+
|
|
496
|
+
Would you like to change your selected bucket or keep the current one?"""
|
|
497
|
+
prompt_text = "Change selected Griptape Cloud bucket?"
|
|
498
|
+
default_value = False
|
|
499
|
+
else:
|
|
500
|
+
explainer = """[bold cyan]Griptape Cloud Bucket Configuration[/bold cyan]
|
|
501
|
+
Would you like to configure a Griptape Cloud bucket?
|
|
502
|
+
Buckets are used for multi-machine workflow and asset syncing, allowing you to:
|
|
503
|
+
- Share workflows and assets across multiple devices
|
|
504
|
+
- Sync generated content between different Griptape Nodes instances
|
|
505
|
+
- Access your work from anywhere
|
|
506
|
+
|
|
507
|
+
If you do not intend to use Griptape Nodes to collaborate or revision control your workflows, you can skip this step.
|
|
508
|
+
|
|
509
|
+
You can always configure a bucket later by running the initialization process again."""
|
|
510
|
+
prompt_text = "Configure Griptape Cloud bucket?"
|
|
511
|
+
default_value = False
|
|
512
|
+
|
|
513
|
+
console.print(Panel(explainer, expand=False))
|
|
514
|
+
return Confirm.ask(prompt_text, default=default_value)
|
|
515
|
+
|
|
516
|
+
|
|
480
517
|
def _prompt_for_gtc_bucket_name(default_bucket_name: str | None = None) -> str:
|
|
481
518
|
"""Prompts the user for a GTC bucket and returns the bucket ID."""
|
|
482
519
|
explainer = """[bold cyan]Storage Backend Bucket Selection[/bold cyan]
|
|
@@ -65,25 +65,52 @@ class ParameterType:
|
|
|
65
65
|
return ret_val
|
|
66
66
|
|
|
67
67
|
@staticmethod
|
|
68
|
-
def
|
|
68
|
+
def _extract_base_type(type_str: str) -> str:
|
|
69
|
+
"""Extract the base type from a potentially generic type string.
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
'list[any]' -> 'list'
|
|
73
|
+
'dict[str, int]' -> 'dict'
|
|
74
|
+
'str' -> 'str'
|
|
75
|
+
"""
|
|
76
|
+
bracket_index = type_str.find("[")
|
|
77
|
+
if bracket_index == -1:
|
|
78
|
+
return type_str
|
|
79
|
+
return type_str[:bracket_index]
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def are_types_compatible(source_type: str | None, target_type: str | None) -> bool: # noqa: PLR0911
|
|
69
83
|
if source_type is None or target_type is None:
|
|
70
84
|
return False
|
|
71
85
|
|
|
72
|
-
ret_val = False
|
|
73
86
|
source_type_lower = source_type.lower()
|
|
74
87
|
target_type_lower = target_type.lower()
|
|
75
88
|
|
|
76
89
|
# If either are None, bail.
|
|
77
90
|
if ParameterTypeBuiltin.NONE.value in (source_type_lower, target_type_lower):
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
return False
|
|
92
|
+
if target_type_lower == ParameterTypeBuiltin.ANY.value:
|
|
80
93
|
# If the TARGET accepts Any, we're good. Not always true the other way 'round.
|
|
81
|
-
|
|
82
|
-
else:
|
|
83
|
-
# Do a compare.
|
|
84
|
-
ret_val = source_type_lower == target_type_lower
|
|
94
|
+
return True
|
|
85
95
|
|
|
86
|
-
|
|
96
|
+
# First try exact match
|
|
97
|
+
if source_type_lower == target_type_lower:
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
source_base = ParameterType._extract_base_type(source_type_lower)
|
|
101
|
+
target_base = ParameterType._extract_base_type(target_type_lower)
|
|
102
|
+
|
|
103
|
+
# If base types match
|
|
104
|
+
if source_base == target_base:
|
|
105
|
+
# Allow any generic to flow to base type (list[any] -> list, list[str] -> list)
|
|
106
|
+
if target_type_lower == target_base:
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
# Allow specific types to flow to [any] generic (list[str] -> list[any])
|
|
110
|
+
if target_type_lower == f"{target_base}[{ParameterTypeBuiltin.ANY.value}]":
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
return False
|
|
87
114
|
|
|
88
115
|
@staticmethod
|
|
89
116
|
def parse_kv_type_pair(type_str: str) -> KeyValueTypePair | None: # noqa: C901
|
|
@@ -250,7 +277,6 @@ class BaseNodeElement:
|
|
|
250
277
|
self._changes["ui_options"] = complete_dict["ui_options"]
|
|
251
278
|
|
|
252
279
|
event_data.update(self._changes)
|
|
253
|
-
|
|
254
280
|
# Publish the event
|
|
255
281
|
event = ExecutionGriptapeNodeEvent(
|
|
256
282
|
wrapped_event=ExecutionEvent(payload=AlterElementEvent(element_details=event_data))
|
|
@@ -1326,6 +1352,27 @@ class ParameterList(ParameterContainer):
|
|
|
1326
1352
|
result = f"list[{base_type}]"
|
|
1327
1353
|
return result
|
|
1328
1354
|
|
|
1355
|
+
def _custom_setter_for_property_type(self, value: str | None) -> None:
|
|
1356
|
+
# If we are setting a type, we need to propagate this to our children as well.
|
|
1357
|
+
for child in self._children:
|
|
1358
|
+
if isinstance(child, Parameter):
|
|
1359
|
+
child.type = value
|
|
1360
|
+
super()._custom_setter_for_property_type(value)
|
|
1361
|
+
|
|
1362
|
+
def _custom_setter_for_property_input_types(self, value: list[str] | None) -> None:
|
|
1363
|
+
# If we are setting a type, we need to propagate this to our children as well.
|
|
1364
|
+
for child in self._children:
|
|
1365
|
+
if isinstance(child, Parameter):
|
|
1366
|
+
child.input_types = value
|
|
1367
|
+
return super()._custom_setter_for_property_input_types(value)
|
|
1368
|
+
|
|
1369
|
+
def _custom_setter_for_property_output_type(self, value: str | None) -> None:
|
|
1370
|
+
# If we are setting a type, we need to propagate this to our children as well.
|
|
1371
|
+
for child in self._children:
|
|
1372
|
+
if isinstance(child, Parameter):
|
|
1373
|
+
child.output_type = value
|
|
1374
|
+
return super()._custom_setter_for_property_output_type(value)
|
|
1375
|
+
|
|
1329
1376
|
def _custom_getter_for_property_input_types(self) -> list[str]:
|
|
1330
1377
|
# For every valid input type, also accept a list variant of that for the CONTAINER Parameter only.
|
|
1331
1378
|
# Children still use the input types given to them.
|
|
@@ -1395,6 +1442,14 @@ class ParameterList(ParameterContainer):
|
|
|
1395
1442
|
|
|
1396
1443
|
return param
|
|
1397
1444
|
|
|
1445
|
+
def clear_list(self) -> None:
|
|
1446
|
+
"""Remove all children that have been added to the list."""
|
|
1447
|
+
children = self.find_elements_by_type(element_type=Parameter)
|
|
1448
|
+
for child in children:
|
|
1449
|
+
if isinstance(child, Parameter):
|
|
1450
|
+
self.remove_child(child)
|
|
1451
|
+
del child
|
|
1452
|
+
|
|
1398
1453
|
def add_child(self, child: BaseNodeElement) -> None:
|
|
1399
1454
|
"""Override to mark parent node as unresolved when children are added.
|
|
1400
1455
|
|
|
@@ -848,12 +848,22 @@ class BaseNode(ABC):
|
|
|
848
848
|
|
|
849
849
|
# Create event data using the parameter's to_event method
|
|
850
850
|
if remove:
|
|
851
|
+
# Import logger here to avoid circular dependency
|
|
852
|
+
from griptape_nodes.retained_mode.griptape_nodes import logger
|
|
853
|
+
|
|
854
|
+
logger.info(
|
|
855
|
+
f"RemoveElementEvent: Emitting parameter lifecycle event for element {parameter.name} on node {self.name}"
|
|
856
|
+
)
|
|
851
857
|
event = ExecutionGriptapeNodeEvent(
|
|
852
858
|
wrapped_event=ExecutionEvent(payload=RemoveElementEvent(element_id=parameter.element_id))
|
|
853
859
|
)
|
|
854
860
|
else:
|
|
855
861
|
event_data = parameter.to_event(self)
|
|
862
|
+
from griptape_nodes.retained_mode.griptape_nodes import logger
|
|
856
863
|
|
|
864
|
+
logger.info(
|
|
865
|
+
f"AlterElementEvent: Emitting parameter lifecycle eventfor element {parameter.name} on node {self.name}"
|
|
866
|
+
)
|
|
857
867
|
# Publish the event
|
|
858
868
|
event = ExecutionGriptapeNodeEvent(
|
|
859
869
|
wrapped_event=ExecutionEvent(payload=AlterElementEvent(element_details=event_data))
|
|
@@ -225,16 +225,18 @@ class ExecuteNodeState(State):
|
|
|
225
225
|
# If the upstream node is resolved, collect its output value
|
|
226
226
|
if upstream_parameter.name in upstream_node.parameter_output_values:
|
|
227
227
|
output_value = upstream_node.parameter_output_values[upstream_parameter.name]
|
|
228
|
+
else:
|
|
229
|
+
output_value = upstream_node.get_parameter_value(upstream_parameter.name)
|
|
228
230
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
)
|
|
231
|
+
# Pass the value through using the same mechanism as normal resolution
|
|
232
|
+
GriptapeNodes.get_instance().handle_request(
|
|
233
|
+
SetParameterValueRequest(
|
|
234
|
+
parameter_name=parameter.name,
|
|
235
|
+
node_name=current_node.name,
|
|
236
|
+
value=output_value,
|
|
237
|
+
data_type=upstream_parameter.output_type,
|
|
237
238
|
)
|
|
239
|
+
)
|
|
238
240
|
|
|
239
241
|
@staticmethod
|
|
240
242
|
def on_enter(context: ResolutionContext) -> type[State] | None:
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import logging
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from dataclasses import asdict, dataclass, field, is_dataclass
|
|
6
|
-
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Literal, TypeVar
|
|
7
8
|
|
|
8
9
|
from griptape.artifacts import BaseArtifact
|
|
9
10
|
from griptape.events import BaseEvent as GtBaseEvent
|
|
@@ -16,6 +17,64 @@ if TYPE_CHECKING:
|
|
|
16
17
|
import builtins
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
@dataclass
|
|
21
|
+
class ResultDetail:
|
|
22
|
+
"""A single detail about an operation result, including logging level and human readable message."""
|
|
23
|
+
|
|
24
|
+
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
25
|
+
message: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ResultDetails:
|
|
30
|
+
"""Container for multiple ResultDetail objects."""
|
|
31
|
+
|
|
32
|
+
result_details: list[ResultDetail]
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*result_details: ResultDetail,
|
|
37
|
+
message: str | None = None,
|
|
38
|
+
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | None = None,
|
|
39
|
+
logger: logging.Logger | str | None = "griptape_nodes",
|
|
40
|
+
):
|
|
41
|
+
"""Initialize with ResultDetail objects or create a single one from message/level.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
*result_details: Variable number of ResultDetail objects
|
|
45
|
+
message: If provided, creates a single ResultDetail with this message
|
|
46
|
+
level: Logging level for the single ResultDetail (required if message is provided)
|
|
47
|
+
logger: Logger to use for auto-logging. String for logger name, Logger object, or None to skip
|
|
48
|
+
"""
|
|
49
|
+
# Handle single message/level convenience
|
|
50
|
+
if message is not None:
|
|
51
|
+
if level is None:
|
|
52
|
+
err_msg = "level is required when message is provided"
|
|
53
|
+
raise ValueError(err_msg)
|
|
54
|
+
if result_details:
|
|
55
|
+
err_msg = "Cannot provide both result_details and message/level"
|
|
56
|
+
raise ValueError(err_msg)
|
|
57
|
+
self.result_details = [ResultDetail(level=level, message=message)]
|
|
58
|
+
else:
|
|
59
|
+
if not result_details:
|
|
60
|
+
err_msg = "ResultDetails requires at least one ResultDetail or message/level"
|
|
61
|
+
raise ValueError(err_msg)
|
|
62
|
+
self.result_details = list(result_details)
|
|
63
|
+
|
|
64
|
+
# Auto-log if logger is provided
|
|
65
|
+
if logger is not None:
|
|
66
|
+
try:
|
|
67
|
+
if isinstance(logger, str):
|
|
68
|
+
logger = logging.getLogger(logger)
|
|
69
|
+
|
|
70
|
+
for detail in self.result_details:
|
|
71
|
+
numeric_level = getattr(logging, detail.level)
|
|
72
|
+
logger.log(numeric_level, detail.message)
|
|
73
|
+
except Exception: # noqa: S110
|
|
74
|
+
# If logging fails for any reason, don't let it break the ResultDetails creation
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
|
|
19
78
|
# The Payload class is a marker interface
|
|
20
79
|
class Payload(ABC): # noqa: B024
|
|
21
80
|
"""Base class for all payload types. Customers will derive from this."""
|
|
@@ -32,6 +91,7 @@ class RequestPayload(Payload, ABC):
|
|
|
32
91
|
class ResultPayload(Payload, ABC):
|
|
33
92
|
"""Base class for all result payloads."""
|
|
34
93
|
|
|
94
|
+
result_details: ResultDetails | str
|
|
35
95
|
"""When set to True, alerts clients that this result made changes to the workflow state.
|
|
36
96
|
Editors can use this to determine if the workflow is dirty and needs to be re-saved, for example."""
|
|
37
97
|
altered_workflow_state: bool = False
|
|
@@ -76,6 +136,13 @@ class SkipTheLineMixin:
|
|
|
76
136
|
class ResultPayloadSuccess(ResultPayload, ABC):
|
|
77
137
|
"""Abstract base class for success result payloads."""
|
|
78
138
|
|
|
139
|
+
result_details: ResultDetails | str = "Success"
|
|
140
|
+
|
|
141
|
+
def __post_init__(self) -> None:
|
|
142
|
+
"""Initialize success result with INFO level default for strings."""
|
|
143
|
+
if isinstance(self.result_details, str):
|
|
144
|
+
self.result_details = ResultDetails(message=self.result_details, level="DEBUG")
|
|
145
|
+
|
|
79
146
|
def succeeded(self) -> bool:
|
|
80
147
|
"""Returns True as this is a success result.
|
|
81
148
|
|
|
@@ -90,8 +157,14 @@ class ResultPayloadSuccess(ResultPayload, ABC):
|
|
|
90
157
|
class ResultPayloadFailure(ResultPayload, ABC):
|
|
91
158
|
"""Abstract base class for failure result payloads."""
|
|
92
159
|
|
|
160
|
+
result_details: ResultDetails | str = "Failure"
|
|
93
161
|
exception: Exception | None = None
|
|
94
162
|
|
|
163
|
+
def __post_init__(self) -> None:
|
|
164
|
+
"""Initialize failure result with ERROR level default for strings."""
|
|
165
|
+
if isinstance(self.result_details, str):
|
|
166
|
+
self.result_details = ResultDetails(message=self.result_details, level="ERROR")
|
|
167
|
+
|
|
95
168
|
def succeeded(self) -> bool:
|
|
96
169
|
"""Returns False as this is a failure result.
|
|
97
170
|
|
|
@@ -20,11 +20,13 @@ class GetSecretValueRequest(RequestPayload):
|
|
|
20
20
|
|
|
21
21
|
Args:
|
|
22
22
|
key: Name of the secret key to retrieve
|
|
23
|
+
should_error_on_not_found: Whether to error if the key is not found (default: True)
|
|
23
24
|
|
|
24
25
|
Results: GetSecretValueResultSuccess (with value) | GetSecretValueResultFailure (key not found)
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
28
|
key: str
|
|
29
|
+
should_error_on_not_found: bool = True
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
@dataclass
|
|
@@ -31,7 +31,7 @@ class RunWorkflowFromScratchRequest(RequestPayload):
|
|
|
31
31
|
|
|
32
32
|
@dataclass
|
|
33
33
|
@PayloadRegistry.register
|
|
34
|
-
class RunWorkflowFromScratchResultSuccess(
|
|
34
|
+
class RunWorkflowFromScratchResultSuccess(ResultPayloadSuccess):
|
|
35
35
|
"""Workflow loaded and started successfully from file."""
|
|
36
36
|
|
|
37
37
|
|
|
@@ -91,7 +91,7 @@ class RunWorkflowFromRegistryRequest(RequestPayload):
|
|
|
91
91
|
|
|
92
92
|
@dataclass
|
|
93
93
|
@PayloadRegistry.register
|
|
94
|
-
class RunWorkflowFromRegistryResultSuccess(
|
|
94
|
+
class RunWorkflowFromRegistryResultSuccess(ResultPayloadSuccess):
|
|
95
95
|
"""Workflow from registry started successfully."""
|
|
96
96
|
|
|
97
97
|
|
|
@@ -347,6 +347,8 @@ class PublishWorkflowRequest(RequestPayload):
|
|
|
347
347
|
|
|
348
348
|
workflow_name: str
|
|
349
349
|
publisher_name: str
|
|
350
|
+
execute_on_publish: bool = False
|
|
351
|
+
published_workflow_file_name: str | None = None
|
|
350
352
|
|
|
351
353
|
|
|
352
354
|
@dataclass
|
|
@@ -389,11 +389,11 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
389
389
|
)
|
|
390
390
|
details = f"Attempted to get engine version. Failed because version string '{engine_ver}' wasn't in expected major.minor.patch format."
|
|
391
391
|
logger.error(details)
|
|
392
|
-
return GetEngineVersionResultFailure()
|
|
392
|
+
return GetEngineVersionResultFailure(result_details=details)
|
|
393
393
|
except Exception as err:
|
|
394
394
|
details = f"Attempted to get engine version. Failed due to '{err}'."
|
|
395
395
|
logger.error(details)
|
|
396
|
-
return GetEngineVersionResultFailure()
|
|
396
|
+
return GetEngineVersionResultFailure(result_details=details)
|
|
397
397
|
|
|
398
398
|
def handle_session_start_request(self, request: AppStartSessionRequest) -> ResultPayload: # noqa: ARG002
|
|
399
399
|
from griptape_nodes.app.app import subscribe_to_topic
|
|
@@ -434,7 +434,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
434
434
|
except Exception as err:
|
|
435
435
|
details = f"Failed to end session due to '{err}'."
|
|
436
436
|
logger.error(details)
|
|
437
|
-
return AppEndSessionResultFailure()
|
|
437
|
+
return AppEndSessionResultFailure(result_details=details)
|
|
438
438
|
|
|
439
439
|
def handle_get_session_request(self, _: AppGetSessionRequest) -> ResultPayload:
|
|
440
440
|
return AppGetSessionResultSuccess(session_id=GriptapeNodes.SessionManager().get_active_session_id())
|
|
@@ -447,14 +447,17 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
447
447
|
try:
|
|
448
448
|
active_session_id = GriptapeNodes.SessionManager().get_active_session_id()
|
|
449
449
|
if active_session_id is None:
|
|
450
|
-
|
|
451
|
-
|
|
450
|
+
details = "Session heartbeat received but no active session found"
|
|
451
|
+
logger.warning(details)
|
|
452
|
+
return SessionHeartbeatResultFailure(result_details=details)
|
|
452
453
|
|
|
453
|
-
|
|
454
|
+
details = f"Session heartbeat successful for session: {active_session_id}"
|
|
455
|
+
logger.debug(details)
|
|
454
456
|
return SessionHeartbeatResultSuccess()
|
|
455
457
|
except Exception as err:
|
|
456
|
-
|
|
457
|
-
|
|
458
|
+
details = f"Failed to handle session heartbeat: {err}"
|
|
459
|
+
logger.error(details)
|
|
460
|
+
return SessionHeartbeatResultFailure(result_details=details)
|
|
458
461
|
|
|
459
462
|
def handle_engine_heartbeat_request(self, request: EngineHeartbeatRequest) -> ResultPayload:
|
|
460
463
|
"""Handle engine heartbeat requests.
|
|
@@ -483,8 +486,9 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
483
486
|
**workflow_info,
|
|
484
487
|
)
|
|
485
488
|
except Exception as err:
|
|
486
|
-
|
|
487
|
-
|
|
489
|
+
details = f"Failed to handle engine heartbeat: {err}"
|
|
490
|
+
logger.error(details)
|
|
491
|
+
return EngineHeartbeatResultFailure(heartbeat_id=request.heartbeat_id, result_details=details)
|
|
488
492
|
|
|
489
493
|
def handle_get_engine_name_request(self, request: GetEngineNameRequest) -> ResultPayload: # noqa: ARG002
|
|
490
494
|
"""Handle requests to get the current engine name."""
|
|
@@ -495,7 +499,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
495
499
|
except Exception as err:
|
|
496
500
|
error_message = f"Failed to get engine name: {err}"
|
|
497
501
|
logger.error(error_message)
|
|
498
|
-
return GetEngineNameResultFailure(error_message=error_message)
|
|
502
|
+
return GetEngineNameResultFailure(error_message=error_message, result_details=error_message)
|
|
499
503
|
|
|
500
504
|
def handle_set_engine_name_request(self, request: SetEngineNameRequest) -> ResultPayload:
|
|
501
505
|
"""Handle requests to set a new engine name."""
|
|
@@ -504,7 +508,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
504
508
|
if not request.engine_name or not request.engine_name.strip():
|
|
505
509
|
error_message = "Engine name cannot be empty"
|
|
506
510
|
logger.warning(error_message)
|
|
507
|
-
return SetEngineNameResultFailure(error_message=error_message)
|
|
511
|
+
return SetEngineNameResultFailure(error_message=error_message, result_details=error_message)
|
|
508
512
|
|
|
509
513
|
# Set the new engine name
|
|
510
514
|
GriptapeNodes.EngineIdentityManager().set_engine_name(request.engine_name.strip())
|
|
@@ -514,7 +518,7 @@ class GriptapeNodes(metaclass=SingletonMeta):
|
|
|
514
518
|
except Exception as err:
|
|
515
519
|
error_message = f"Failed to set engine name: {err}"
|
|
516
520
|
logger.error(error_message)
|
|
517
|
-
return SetEngineNameResultFailure(error_message=error_message)
|
|
521
|
+
return SetEngineNameResultFailure(error_message=error_message, result_details=error_message)
|
|
518
522
|
|
|
519
523
|
def _get_instance_info(self) -> dict[str, str | None]:
|
|
520
524
|
"""Get instance information from environment variables.
|
|
@@ -204,16 +204,18 @@ class AgentManager:
|
|
|
204
204
|
pass # Ignore incomplete JSON
|
|
205
205
|
if isinstance(last_event, FinishTaskEvent):
|
|
206
206
|
if isinstance(last_event.task_output, ErrorArtifact):
|
|
207
|
-
return RunAgentResultFailure(
|
|
207
|
+
return RunAgentResultFailure(
|
|
208
|
+
error=last_event.task_output.to_dict(), result_details=last_event.task_output.to_json()
|
|
209
|
+
)
|
|
208
210
|
if isinstance(last_event.task_output, JsonArtifact):
|
|
209
211
|
return RunAgentResultSuccess(last_event.task_output.to_dict())
|
|
210
212
|
err_msg = f"Unexpected final event: {last_event}"
|
|
211
213
|
logger.error(err_msg)
|
|
212
|
-
return RunAgentResultFailure(ErrorArtifact(last_event).to_dict())
|
|
214
|
+
return RunAgentResultFailure(error=ErrorArtifact(last_event).to_dict(), result_details=err_msg)
|
|
213
215
|
except Exception as e:
|
|
214
216
|
err_msg = f"Error running agent: {e}"
|
|
215
217
|
logger.error(err_msg)
|
|
216
|
-
return RunAgentResultFailure(ErrorArtifact(e).to_dict())
|
|
218
|
+
return RunAgentResultFailure(error=ErrorArtifact(e).to_dict(), result_details=err_msg)
|
|
217
219
|
|
|
218
220
|
def on_handle_configure_agent_request(self, request: ConfigureAgentRequest) -> ResultPayload:
|
|
219
221
|
try:
|
|
@@ -224,7 +226,7 @@ class AgentManager:
|
|
|
224
226
|
except Exception as e:
|
|
225
227
|
details = f"Error configuring agent: {e}"
|
|
226
228
|
logger.error(details)
|
|
227
|
-
return ConfigureAgentResultFailure()
|
|
229
|
+
return ConfigureAgentResultFailure(result_details=details)
|
|
228
230
|
return ConfigureAgentResultSuccess()
|
|
229
231
|
|
|
230
232
|
def on_handle_reset_agent_conversation_memory_request(
|
|
@@ -235,7 +237,7 @@ class AgentManager:
|
|
|
235
237
|
except Exception as e:
|
|
236
238
|
details = f"Error resetting agent conversation memory: {e}"
|
|
237
239
|
logger.error(details)
|
|
238
|
-
return ResetAgentConversationMemoryResultFailure()
|
|
240
|
+
return ResetAgentConversationMemoryResultFailure(result_details=details)
|
|
239
241
|
return ResetAgentConversationMemoryResultSuccess()
|
|
240
242
|
|
|
241
243
|
def on_handle_get_conversation_memory_request(self, _: GetConversationMemoryRequest) -> ResultPayload:
|
|
@@ -244,5 +246,5 @@ class AgentManager:
|
|
|
244
246
|
except Exception as e:
|
|
245
247
|
details = f"Error getting conversation memory: {e}"
|
|
246
248
|
logger.error(details)
|
|
247
|
-
return GetConversationMemoryResultFailure()
|
|
249
|
+
return GetConversationMemoryResultFailure(result_details=details)
|
|
248
250
|
return GetConversationMemoryResultSuccess(runs=conversation_memory)
|
|
@@ -46,6 +46,6 @@ class ArbitraryCodeExecManager:
|
|
|
46
46
|
result = RunArbitraryPythonStringResultSuccess(python_output=captured_output)
|
|
47
47
|
except Exception as e:
|
|
48
48
|
python_output = f"ERROR: {e}"
|
|
49
|
-
result = RunArbitraryPythonStringResultFailure(python_output=python_output)
|
|
49
|
+
result = RunArbitraryPythonStringResultFailure(python_output=python_output, result_details=python_output)
|
|
50
50
|
|
|
51
51
|
return result
|