griptape-nodes 0.51.2__py3-none-any.whl → 0.52.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 +5 -4
- griptape_nodes/app/api.py +27 -24
- griptape_nodes/app/app.py +243 -221
- griptape_nodes/app/watch.py +17 -2
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +66 -103
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +16 -4
- griptape_nodes/exe_types/core_types.py +16 -4
- griptape_nodes/exe_types/node_types.py +74 -16
- griptape_nodes/machines/control_flow.py +21 -26
- griptape_nodes/machines/fsm.py +16 -16
- griptape_nodes/machines/node_resolution.py +30 -119
- griptape_nodes/mcp_server/server.py +14 -10
- griptape_nodes/mcp_server/ws_request_manager.py +2 -2
- griptape_nodes/node_library/workflow_registry.py +5 -0
- griptape_nodes/retained_mode/events/base_events.py +12 -7
- griptape_nodes/retained_mode/events/execution_events.py +0 -6
- griptape_nodes/retained_mode/events/node_events.py +38 -0
- griptape_nodes/retained_mode/events/parameter_events.py +11 -0
- griptape_nodes/retained_mode/events/variable_events.py +361 -0
- griptape_nodes/retained_mode/events/workflow_events.py +35 -0
- griptape_nodes/retained_mode/griptape_nodes.py +61 -26
- griptape_nodes/retained_mode/managers/agent_manager.py +8 -9
- griptape_nodes/retained_mode/managers/event_manager.py +215 -74
- griptape_nodes/retained_mode/managers/flow_manager.py +39 -33
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +14 -14
- griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +20 -20
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +1 -1
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +1 -1
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +4 -3
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +1 -1
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +1 -1
- griptape_nodes/retained_mode/managers/library_manager.py +20 -19
- griptape_nodes/retained_mode/managers/node_manager.py +83 -8
- griptape_nodes/retained_mode/managers/object_manager.py +4 -0
- griptape_nodes/retained_mode/managers/settings.py +1 -0
- griptape_nodes/retained_mode/managers/sync_manager.py +3 -9
- griptape_nodes/retained_mode/managers/variable_manager.py +529 -0
- griptape_nodes/retained_mode/managers/workflow_manager.py +156 -50
- griptape_nodes/retained_mode/variable_types.py +18 -0
- griptape_nodes/utils/__init__.py +4 -0
- griptape_nodes/utils/async_utils.py +89 -0
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.0.dist-info}/METADATA +2 -3
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.0.dist-info}/RECORD +45 -42
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.0.dist-info}/WHEEL +1 -1
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +0 -90
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
+
import asyncio
|
|
4
5
|
import logging
|
|
5
6
|
import pickle
|
|
6
7
|
import pkgutil
|
|
@@ -57,6 +58,9 @@ from griptape_nodes.retained_mode.events.workflow_events import (
|
|
|
57
58
|
ImportWorkflowAsReferencedSubFlowRequest,
|
|
58
59
|
ImportWorkflowAsReferencedSubFlowResultFailure,
|
|
59
60
|
ImportWorkflowAsReferencedSubFlowResultSuccess,
|
|
61
|
+
ImportWorkflowRequest,
|
|
62
|
+
ImportWorkflowResultFailure,
|
|
63
|
+
ImportWorkflowResultSuccess,
|
|
60
64
|
ListAllWorkflowsRequest,
|
|
61
65
|
ListAllWorkflowsResultFailure,
|
|
62
66
|
ListAllWorkflowsResultSuccess,
|
|
@@ -271,6 +275,10 @@ class WorkflowManager:
|
|
|
271
275
|
ImportWorkflowAsReferencedSubFlowRequest,
|
|
272
276
|
self.on_import_workflow_as_referenced_sub_flow_request,
|
|
273
277
|
)
|
|
278
|
+
event_manager.assign_manager_to_request_type(
|
|
279
|
+
ImportWorkflowRequest,
|
|
280
|
+
self.on_import_workflow_request,
|
|
281
|
+
)
|
|
274
282
|
event_manager.assign_manager_to_request_type(
|
|
275
283
|
BranchWorkflowRequest,
|
|
276
284
|
self.on_branch_workflow_request,
|
|
@@ -621,6 +629,38 @@ class WorkflowManager:
|
|
|
621
629
|
return RegisterWorkflowResultFailure(result_details=details)
|
|
622
630
|
return RegisterWorkflowResultSuccess(workflow_name=workflow.metadata.name)
|
|
623
631
|
|
|
632
|
+
def on_import_workflow_request(self, request: ImportWorkflowRequest) -> ResultPayload:
|
|
633
|
+
# First, attempt to load metadata from the file
|
|
634
|
+
load_metadata_request = LoadWorkflowMetadata(file_name=request.file_path)
|
|
635
|
+
load_metadata_result = self.on_load_workflow_metadata_request(load_metadata_request)
|
|
636
|
+
|
|
637
|
+
if not isinstance(load_metadata_result, LoadWorkflowMetadataResultSuccess):
|
|
638
|
+
return ImportWorkflowResultFailure(result_details=load_metadata_result.result_details)
|
|
639
|
+
|
|
640
|
+
# Check if workflow is already registered
|
|
641
|
+
workflow_name = load_metadata_result.metadata.name
|
|
642
|
+
if WorkflowRegistry.has_workflow_with_name(workflow_name):
|
|
643
|
+
# Workflow already exists - no need to re-register
|
|
644
|
+
return ImportWorkflowResultSuccess(workflow_name=workflow_name)
|
|
645
|
+
|
|
646
|
+
# Now register the workflow with the extracted metadata
|
|
647
|
+
register_request = RegisterWorkflowRequest(metadata=load_metadata_result.metadata, file_name=request.file_path)
|
|
648
|
+
register_result = self.on_register_workflow_request(register_request)
|
|
649
|
+
|
|
650
|
+
if not isinstance(register_result, RegisterWorkflowResultSuccess):
|
|
651
|
+
return ImportWorkflowResultFailure(result_details=register_result.result_details)
|
|
652
|
+
|
|
653
|
+
# Add the workflow to the user configuration
|
|
654
|
+
try:
|
|
655
|
+
full_path = WorkflowRegistry.get_complete_file_path(request.file_path)
|
|
656
|
+
GriptapeNodes.ConfigManager().save_user_workflow_json(full_path)
|
|
657
|
+
except Exception as e:
|
|
658
|
+
details = f"Failed to add workflow '{register_result.workflow_name}' to user configuration: {e}"
|
|
659
|
+
|
|
660
|
+
return ImportWorkflowResultFailure(result_details=details)
|
|
661
|
+
|
|
662
|
+
return ImportWorkflowResultSuccess(workflow_name=register_result.workflow_name)
|
|
663
|
+
|
|
624
664
|
def on_list_all_workflows_request(self, _request: ListAllWorkflowsRequest) -> ResultPayload:
|
|
625
665
|
try:
|
|
626
666
|
workflows = WorkflowRegistry.list_workflows()
|
|
@@ -1358,10 +1398,20 @@ class WorkflowManager:
|
|
|
1358
1398
|
file_name = new_file_name
|
|
1359
1399
|
|
|
1360
1400
|
# Get file name stuff prepped.
|
|
1361
|
-
if not
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1401
|
+
# Use the existing registered file path if this is an existing workflow (not a template)
|
|
1402
|
+
if prior_workflow and not prior_workflow.metadata.is_template:
|
|
1403
|
+
# Use the existing registered file path
|
|
1404
|
+
relative_file_path = prior_workflow.file_path
|
|
1405
|
+
file_path = Path(WorkflowRegistry.get_complete_file_path(relative_file_path))
|
|
1406
|
+
# Extract file name from the path for metadata generation
|
|
1407
|
+
if not file_name:
|
|
1408
|
+
file_name = prior_workflow.metadata.name
|
|
1409
|
+
else:
|
|
1410
|
+
# Create new path in workspace for new workflows or templates
|
|
1411
|
+
if not file_name:
|
|
1412
|
+
file_name = datetime.now(tz=UTC).strftime("%d.%m_%H.%M")
|
|
1413
|
+
relative_file_path = f"{file_name}.py"
|
|
1414
|
+
file_path = GriptapeNodes.ConfigManager().workspace_path.joinpath(relative_file_path)
|
|
1365
1415
|
|
|
1366
1416
|
# Generate the workflow file contents
|
|
1367
1417
|
try:
|
|
@@ -1384,20 +1434,17 @@ class WorkflowManager:
|
|
|
1384
1434
|
logger.error(details)
|
|
1385
1435
|
return SaveWorkflowResultFailure(result_details=details)
|
|
1386
1436
|
|
|
1387
|
-
relative_serialized_file_path = f"{file_name}.py"
|
|
1388
|
-
serialized_file_path = GriptapeNodes.ConfigManager().workspace_path.joinpath(relative_serialized_file_path)
|
|
1389
|
-
|
|
1390
1437
|
# Check disk space before writing
|
|
1391
1438
|
config_manager = GriptapeNodes.ConfigManager()
|
|
1392
1439
|
min_space_gb = config_manager.get_config_value("minimum_disk_space_gb_workflows")
|
|
1393
|
-
if not OSManager.check_available_disk_space(
|
|
1394
|
-
error_msg = OSManager.format_disk_space_error(
|
|
1440
|
+
if not OSManager.check_available_disk_space(file_path.parent, min_space_gb):
|
|
1441
|
+
error_msg = OSManager.format_disk_space_error(file_path.parent)
|
|
1395
1442
|
details = f"Attempted to save workflow '{file_name}' (requires {min_space_gb:.1f} GB). Failed: {error_msg}"
|
|
1396
1443
|
logger.error(details)
|
|
1397
1444
|
return SaveWorkflowResultFailure(result_details=details)
|
|
1398
1445
|
|
|
1399
1446
|
try:
|
|
1400
|
-
with
|
|
1447
|
+
with file_path.open("w", encoding="utf-8") as file:
|
|
1401
1448
|
file.write(final_code_output)
|
|
1402
1449
|
except OSError as e:
|
|
1403
1450
|
details = f"Attempted to save workflow '{file_name}'. Failed when writing file: {e}"
|
|
@@ -1407,19 +1454,21 @@ class WorkflowManager:
|
|
|
1407
1454
|
# save the created workflow as an entry in the JSON config file.
|
|
1408
1455
|
registered_workflows = WorkflowRegistry.list_workflows()
|
|
1409
1456
|
if file_name not in registered_workflows:
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1457
|
+
# Only add to config if it's in the workspace directory (not an external file)
|
|
1458
|
+
if not Path(relative_file_path).is_absolute():
|
|
1459
|
+
try:
|
|
1460
|
+
GriptapeNodes.ConfigManager().save_user_workflow_json(str(file_path))
|
|
1461
|
+
except OSError as e:
|
|
1462
|
+
details = f"Attempted to save workflow '{file_name}'. Failed when saving configuration: {e}"
|
|
1463
|
+
logger.error(details)
|
|
1464
|
+
return SaveWorkflowResultFailure(result_details=details)
|
|
1416
1465
|
WorkflowRegistry.generate_new_workflow(metadata=workflow_metadata, file_path=relative_file_path)
|
|
1417
1466
|
# Update existing workflow's metadata in the registry
|
|
1418
1467
|
existing_workflow = WorkflowRegistry.get_workflow_by_name(file_name)
|
|
1419
1468
|
existing_workflow.metadata = workflow_metadata
|
|
1420
|
-
details = f"Successfully saved workflow to: {
|
|
1469
|
+
details = f"Successfully saved workflow to: {file_path}"
|
|
1421
1470
|
logger.info(details)
|
|
1422
|
-
return SaveWorkflowResultSuccess(file_path=str(
|
|
1471
|
+
return SaveWorkflowResultSuccess(file_path=str(file_path))
|
|
1423
1472
|
|
|
1424
1473
|
def _generate_workflow_metadata( # noqa: PLR0913
|
|
1425
1474
|
self,
|
|
@@ -1515,6 +1564,7 @@ class WorkflowManager:
|
|
|
1515
1564
|
|
|
1516
1565
|
# === imports ===
|
|
1517
1566
|
import_recorder.add_import("argparse")
|
|
1567
|
+
import_recorder.add_import("asyncio")
|
|
1518
1568
|
import_recorder.add_import("json")
|
|
1519
1569
|
import_recorder.add_from_import(
|
|
1520
1570
|
"griptape_nodes.bootstrap.workflow_executors.local_workflow_executor", "LocalWorkflowExecutor"
|
|
@@ -1522,6 +1572,7 @@ class WorkflowManager:
|
|
|
1522
1572
|
import_recorder.add_from_import(
|
|
1523
1573
|
"griptape_nodes.bootstrap.workflow_executors.workflow_executor", "WorkflowExecutor"
|
|
1524
1574
|
)
|
|
1575
|
+
import_recorder.add_from_import("griptape_nodes.drivers.storage.storage_backend", "StorageBackend")
|
|
1525
1576
|
|
|
1526
1577
|
# === 1) build the `def execute_workflow(input: dict, storage_backend: str = StorageBackend.LOCAL, workflow_executor: WorkflowExecutor | None = None) -> dict | None:` ===
|
|
1527
1578
|
# args
|
|
@@ -1554,6 +1605,16 @@ class WorkflowManager:
|
|
|
1554
1605
|
# Generate the ensure flow context function call
|
|
1555
1606
|
ensure_context_call = self._generate_ensure_flow_context_call()
|
|
1556
1607
|
|
|
1608
|
+
# Convert string storage_backend to StorageBackend enum
|
|
1609
|
+
storage_backend_convert = ast.Assign(
|
|
1610
|
+
targets=[ast.Name(id="storage_backend_enum", ctx=ast.Store())],
|
|
1611
|
+
value=ast.Call(
|
|
1612
|
+
func=ast.Name(id="StorageBackend", ctx=ast.Load()),
|
|
1613
|
+
args=[ast.Name(id="storage_backend", ctx=ast.Load())],
|
|
1614
|
+
keywords=[],
|
|
1615
|
+
),
|
|
1616
|
+
)
|
|
1617
|
+
|
|
1557
1618
|
# Create conditional logic: workflow_executor = workflow_executor or LocalWorkflowExecutor()
|
|
1558
1619
|
executor_assign = ast.Assign(
|
|
1559
1620
|
targets=[ast.Name(id="workflow_executor", ctx=ast.Store())],
|
|
@@ -1570,18 +1631,20 @@ class WorkflowManager:
|
|
|
1570
1631
|
),
|
|
1571
1632
|
)
|
|
1572
1633
|
run_call = ast.Expr(
|
|
1573
|
-
value=ast.
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1634
|
+
value=ast.Await(
|
|
1635
|
+
value=ast.Call(
|
|
1636
|
+
func=ast.Attribute(
|
|
1637
|
+
value=ast.Name(id="workflow_executor", ctx=ast.Load()),
|
|
1638
|
+
attr="arun",
|
|
1639
|
+
ctx=ast.Load(),
|
|
1640
|
+
),
|
|
1641
|
+
args=[],
|
|
1642
|
+
keywords=[
|
|
1643
|
+
ast.keyword(arg="workflow_name", value=ast.Constant(flow_name)),
|
|
1644
|
+
ast.keyword(arg="flow_input", value=ast.Name(id="input", ctx=ast.Load())),
|
|
1645
|
+
ast.keyword(arg="storage_backend", value=ast.Name(id="storage_backend_enum", ctx=ast.Load())),
|
|
1646
|
+
],
|
|
1647
|
+
)
|
|
1585
1648
|
)
|
|
1586
1649
|
)
|
|
1587
1650
|
return_stmt = ast.Return(
|
|
@@ -1592,15 +1655,53 @@ class WorkflowManager:
|
|
|
1592
1655
|
)
|
|
1593
1656
|
)
|
|
1594
1657
|
|
|
1595
|
-
|
|
1658
|
+
# === Generate async aexecute_workflow function ===
|
|
1659
|
+
async_func_def = ast.AsyncFunctionDef(
|
|
1660
|
+
name="aexecute_workflow",
|
|
1661
|
+
args=args,
|
|
1662
|
+
body=[ensure_context_call, storage_backend_convert, executor_assign, run_call, return_stmt],
|
|
1663
|
+
decorator_list=[],
|
|
1664
|
+
returns=return_annotation,
|
|
1665
|
+
type_params=[],
|
|
1666
|
+
)
|
|
1667
|
+
ast.fix_missing_locations(async_func_def)
|
|
1668
|
+
|
|
1669
|
+
# === Generate sync execute_workflow function (backward compatibility wrapper) ===
|
|
1670
|
+
sync_func_def = ast.FunctionDef(
|
|
1596
1671
|
name="execute_workflow",
|
|
1597
1672
|
args=args,
|
|
1598
|
-
body=[
|
|
1673
|
+
body=[
|
|
1674
|
+
ast.Return(
|
|
1675
|
+
value=ast.Call(
|
|
1676
|
+
func=ast.Attribute(
|
|
1677
|
+
value=ast.Name(id="asyncio", ctx=ast.Load()),
|
|
1678
|
+
attr="run",
|
|
1679
|
+
ctx=ast.Load(),
|
|
1680
|
+
),
|
|
1681
|
+
args=[
|
|
1682
|
+
ast.Call(
|
|
1683
|
+
func=ast.Name(id="aexecute_workflow", ctx=ast.Load()),
|
|
1684
|
+
args=[],
|
|
1685
|
+
keywords=[
|
|
1686
|
+
ast.keyword(arg="input", value=ast.Name(id="input", ctx=ast.Load())),
|
|
1687
|
+
ast.keyword(
|
|
1688
|
+
arg="storage_backend", value=ast.Name(id="storage_backend", ctx=ast.Load())
|
|
1689
|
+
),
|
|
1690
|
+
ast.keyword(
|
|
1691
|
+
arg="workflow_executor", value=ast.Name(id="workflow_executor", ctx=ast.Load())
|
|
1692
|
+
),
|
|
1693
|
+
],
|
|
1694
|
+
)
|
|
1695
|
+
],
|
|
1696
|
+
keywords=[],
|
|
1697
|
+
)
|
|
1698
|
+
)
|
|
1699
|
+
],
|
|
1599
1700
|
decorator_list=[],
|
|
1600
1701
|
returns=return_annotation,
|
|
1601
1702
|
type_params=[],
|
|
1602
1703
|
)
|
|
1603
|
-
ast.fix_missing_locations(
|
|
1704
|
+
ast.fix_missing_locations(sync_func_def)
|
|
1604
1705
|
|
|
1605
1706
|
# === 2) build the `if __name__ == "__main__":` block ===
|
|
1606
1707
|
main_test = ast.Compare(
|
|
@@ -1890,7 +1991,7 @@ class WorkflowManager:
|
|
|
1890
1991
|
# Generate the ensure flow context function
|
|
1891
1992
|
ensure_context_func = self._generate_ensure_flow_context_function(import_recorder)
|
|
1892
1993
|
|
|
1893
|
-
return [ensure_context_func,
|
|
1994
|
+
return [ensure_context_func, sync_func_def, async_func_def, if_node]
|
|
1894
1995
|
|
|
1895
1996
|
def _generate_ensure_flow_context_function(
|
|
1896
1997
|
self,
|
|
@@ -2094,6 +2195,9 @@ class WorkflowManager:
|
|
|
2094
2195
|
import_recorder.add_from_import(
|
|
2095
2196
|
"griptape_nodes.retained_mode.events.library_events", "GetAllInfoForAllLibrariesResultSuccess"
|
|
2096
2197
|
)
|
|
2198
|
+
import_recorder.add_from_import(
|
|
2199
|
+
"griptape_nodes.retained_mode.events.library_events", "ReloadAllLibrariesRequest"
|
|
2200
|
+
)
|
|
2097
2201
|
|
|
2098
2202
|
code_blocks: list[ast.AST] = []
|
|
2099
2203
|
|
|
@@ -2168,24 +2272,22 @@ class WorkflowManager:
|
|
|
2168
2272
|
)
|
|
2169
2273
|
ast.fix_missing_locations(test)
|
|
2170
2274
|
|
|
2171
|
-
# 3) the body: GriptapeNodes.
|
|
2275
|
+
# 3) the body: GriptapeNodes.handle_request(ReloadAllLibrariesRequest())
|
|
2172
2276
|
# TODO (https://github.com/griptape-ai/griptape-nodes/issues/1615): Generate requests to load ONLY the libraries used in this workflow
|
|
2173
2277
|
load_call = ast.Expr(
|
|
2174
2278
|
value=ast.Call(
|
|
2175
2279
|
func=ast.Attribute(
|
|
2176
|
-
value=ast.
|
|
2177
|
-
|
|
2178
|
-
value=ast.Name(id="GriptapeNodes", ctx=ast.Load()),
|
|
2179
|
-
attr="LibraryManager",
|
|
2180
|
-
ctx=ast.Load(),
|
|
2181
|
-
),
|
|
2182
|
-
args=[],
|
|
2183
|
-
keywords=[],
|
|
2184
|
-
),
|
|
2185
|
-
attr="load_all_libraries_from_config",
|
|
2280
|
+
value=ast.Name(id="GriptapeNodes", ctx=ast.Load()),
|
|
2281
|
+
attr="handle_request",
|
|
2186
2282
|
ctx=ast.Load(),
|
|
2187
2283
|
),
|
|
2188
|
-
args=[
|
|
2284
|
+
args=[
|
|
2285
|
+
ast.Call(
|
|
2286
|
+
func=ast.Name(id="ReloadAllLibrariesRequest", ctx=ast.Load()),
|
|
2287
|
+
args=[],
|
|
2288
|
+
keywords=[],
|
|
2289
|
+
)
|
|
2290
|
+
],
|
|
2189
2291
|
keywords=[],
|
|
2190
2292
|
)
|
|
2191
2293
|
)
|
|
@@ -2954,7 +3056,8 @@ class WorkflowManager:
|
|
|
2954
3056
|
set_parameter_value_asts.append(with_node_context)
|
|
2955
3057
|
return set_parameter_value_asts
|
|
2956
3058
|
|
|
2957
|
-
|
|
3059
|
+
@classmethod
|
|
3060
|
+
def _convert_parameter_to_minimal_dict(cls, parameter: Parameter) -> dict[str, Any]:
|
|
2958
3061
|
"""Converts a parameter to a minimal dictionary for loading up a dynamic, black-box Node."""
|
|
2959
3062
|
param_dict = parameter.to_dict()
|
|
2960
3063
|
fields_to_include = [
|
|
@@ -2973,9 +3076,12 @@ class WorkflowManager:
|
|
|
2973
3076
|
"traits",
|
|
2974
3077
|
"ui_options",
|
|
2975
3078
|
"settable",
|
|
2976
|
-
"
|
|
3079
|
+
"is_user_defined",
|
|
2977
3080
|
]
|
|
2978
3081
|
minimal_dict = {key: param_dict[key] for key in fields_to_include if key in param_dict}
|
|
3082
|
+
minimal_dict["settable"] = bool(getattr(parameter, "settable", True))
|
|
3083
|
+
minimal_dict["is_user_defined"] = bool(getattr(param_dict, "is_user_defined", True))
|
|
3084
|
+
|
|
2979
3085
|
return minimal_dict
|
|
2980
3086
|
|
|
2981
3087
|
def _create_workflow_shape_from_nodes(
|
|
@@ -3053,7 +3159,7 @@ class WorkflowManager:
|
|
|
3053
3159
|
|
|
3054
3160
|
return workflow_shape
|
|
3055
3161
|
|
|
3056
|
-
def on_publish_workflow_request(self, request: PublishWorkflowRequest) -> ResultPayload:
|
|
3162
|
+
async def on_publish_workflow_request(self, request: PublishWorkflowRequest) -> ResultPayload:
|
|
3057
3163
|
try:
|
|
3058
3164
|
publisher_name = request.publisher_name
|
|
3059
3165
|
event_handler_mappings = GriptapeNodes.LibraryManager().get_registered_event_handlers(
|
|
@@ -3065,7 +3171,7 @@ class WorkflowManager:
|
|
|
3065
3171
|
msg = f"No publishing handler found for '{publisher_name}' in request type '{type(request).__name__}'."
|
|
3066
3172
|
raise ValueError(msg) # noqa: TRY301
|
|
3067
3173
|
|
|
3068
|
-
result = publishing_handler.handler
|
|
3174
|
+
result = await asyncio.to_thread(publishing_handler.handler, request)
|
|
3069
3175
|
if isinstance(result, PublishWorkflowResultSuccess):
|
|
3070
3176
|
file = Path(result.published_workflow_file_path)
|
|
3071
3177
|
self._register_published_workflow_file(file)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class VariableScope(StrEnum):
|
|
7
|
+
CURRENT_FLOW_ONLY = "current_flow_only"
|
|
8
|
+
HIERARCHICAL = "hierarchical"
|
|
9
|
+
GLOBAL_ONLY = "global_only"
|
|
10
|
+
ALL = "all" # For ListVariables to get all variables from all flows
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class FlowVariable:
|
|
15
|
+
name: str
|
|
16
|
+
owning_flow_name: str | None # None for global variables
|
|
17
|
+
type: str
|
|
18
|
+
value: Any
|
griptape_nodes/utils/__init__.py
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Utilities for handling async/sync callback patterns."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import inspect
|
|
7
|
+
import subprocess
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Callable, Sequence
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def call_function(func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
|
|
15
|
+
"""Call a function, handling both sync and async cases.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
func: The function to call (sync or async)
|
|
19
|
+
*args: Positional arguments to pass to the function
|
|
20
|
+
**kwargs: Keyword arguments to pass to the function
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
The result of the function call
|
|
24
|
+
"""
|
|
25
|
+
if inspect.iscoroutinefunction(func):
|
|
26
|
+
return await func(*args, **kwargs)
|
|
27
|
+
return func(*args, **kwargs)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def subprocess_run(
|
|
31
|
+
args: Sequence[str],
|
|
32
|
+
*,
|
|
33
|
+
capture_output: bool = False,
|
|
34
|
+
text: bool = False,
|
|
35
|
+
check: bool = False,
|
|
36
|
+
) -> subprocess.CompletedProcess[str | bytes]:
|
|
37
|
+
"""Run a subprocess asynchronously with an interface similar to subprocess.run().
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
args: Command and arguments to execute
|
|
41
|
+
capture_output: Whether to capture stdout and stderr
|
|
42
|
+
text: Whether to decode output as text
|
|
43
|
+
check: Whether to raise CalledProcessError on non-zero exit
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
CompletedProcess with the result
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
subprocess.CalledProcessError: If check=True and the process exits with non-zero code
|
|
50
|
+
"""
|
|
51
|
+
if capture_output:
|
|
52
|
+
stdout_arg = asyncio.subprocess.PIPE
|
|
53
|
+
stderr_arg = asyncio.subprocess.PIPE
|
|
54
|
+
else:
|
|
55
|
+
stdout_arg = None
|
|
56
|
+
stderr_arg = None
|
|
57
|
+
|
|
58
|
+
process = await asyncio.create_subprocess_exec(
|
|
59
|
+
*args,
|
|
60
|
+
stdout=stdout_arg,
|
|
61
|
+
stderr=stderr_arg,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
stdout_bytes, stderr_bytes = await process.communicate()
|
|
65
|
+
|
|
66
|
+
# Convert bytes to string if text=True
|
|
67
|
+
if text:
|
|
68
|
+
stdout = stdout_bytes.decode() if stdout_bytes else ""
|
|
69
|
+
stderr = stderr_bytes.decode() if stderr_bytes else ""
|
|
70
|
+
else:
|
|
71
|
+
stdout = stdout_bytes if stdout_bytes else b""
|
|
72
|
+
stderr = stderr_bytes if stderr_bytes else b""
|
|
73
|
+
|
|
74
|
+
completed_process = subprocess.CompletedProcess(
|
|
75
|
+
args=list(args),
|
|
76
|
+
returncode=process.returncode or 0,
|
|
77
|
+
stdout=stdout,
|
|
78
|
+
stderr=stderr,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if check and completed_process.returncode != 0:
|
|
82
|
+
raise subprocess.CalledProcessError(
|
|
83
|
+
completed_process.returncode,
|
|
84
|
+
args,
|
|
85
|
+
completed_process.stdout,
|
|
86
|
+
completed_process.stderr,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return completed_process
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: griptape-nodes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.52.0
|
|
4
4
|
Summary: Add your description here
|
|
5
|
-
Requires-Dist: griptape>=1.8.
|
|
5
|
+
Requires-Dist: griptape>=1.8.2
|
|
6
6
|
Requires-Dist: pydantic>=2.10.6
|
|
7
7
|
Requires-Dist: python-dotenv>=1.0.1
|
|
8
8
|
Requires-Dist: xdg-base-dirs>=6.0.2
|
|
@@ -15,7 +15,6 @@ Requires-Dist: uvicorn>=0.34.2
|
|
|
15
15
|
Requires-Dist: packaging>=25.0
|
|
16
16
|
Requires-Dist: python-multipart>=0.0.20
|
|
17
17
|
Requires-Dist: json-repair>=0.46.1
|
|
18
|
-
Requires-Dist: imageio-ffmpeg>=0.6.0
|
|
19
18
|
Requires-Dist: mcp[ws]>=1.10.1
|
|
20
19
|
Requires-Dist: binaryornot>=0.4.4
|
|
21
20
|
Requires-Dist: pillow>=11.3.0
|