griptape-nodes 0.36.2__py3-none-any.whl → 0.37.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 +68 -2
- griptape_nodes/app/__init__.py +1 -1
- griptape_nodes/app/app.py +6 -6
- griptape_nodes/app/app_websocket.py +6 -6
- griptape_nodes/exe_types/connections.py +32 -1
- griptape_nodes/exe_types/core_types.py +16 -0
- griptape_nodes/exe_types/flow.py +2 -2
- griptape_nodes/exe_types/node_types.py +207 -0
- griptape_nodes/retained_mode/events/agent_events.py +83 -0
- griptape_nodes/retained_mode/griptape_nodes.py +8 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +103 -0
- griptape_nodes/retained_mode/managers/library_manager.py +50 -37
- griptape_nodes/retained_mode/managers/node_manager.py +29 -2
- griptape_nodes/retained_mode/retained_mode.py +662 -18
- {griptape_nodes-0.36.2.dist-info → griptape_nodes-0.37.0.dist-info}/METADATA +3 -2
- {griptape_nodes-0.36.2.dist-info → griptape_nodes-0.37.0.dist-info}/RECORD +19 -17
- {griptape_nodes-0.36.2.dist-info → griptape_nodes-0.37.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.36.2.dist-info → griptape_nodes-0.37.0.dist-info}/entry_points.txt +0 -0
- {griptape_nodes-0.36.2.dist-info → griptape_nodes-0.37.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from griptape.artifacts import ErrorArtifact
|
|
4
|
+
from griptape.drivers.prompt.griptape_cloud import GriptapeCloudPromptDriver
|
|
5
|
+
from griptape.memory.structure import ConversationMemory
|
|
6
|
+
from griptape.tasks import PromptTask
|
|
7
|
+
|
|
8
|
+
from griptape_nodes.retained_mode.events.agent_events import (
|
|
9
|
+
ConfigureAgentRequest,
|
|
10
|
+
ConfigureAgentResultFailure,
|
|
11
|
+
ConfigureAgentResultSuccess,
|
|
12
|
+
GetConversationMemoryRequest,
|
|
13
|
+
GetConversationMemoryResultFailure,
|
|
14
|
+
GetConversationMemoryResultSuccess,
|
|
15
|
+
ResetAgentConversationMemoryRequest,
|
|
16
|
+
ResetAgentConversationMemoryResultFailure,
|
|
17
|
+
ResetAgentConversationMemoryResultSuccess,
|
|
18
|
+
RunAgentRequest,
|
|
19
|
+
RunAgentResultFailure,
|
|
20
|
+
RunAgentResultSuccess,
|
|
21
|
+
)
|
|
22
|
+
from griptape_nodes.retained_mode.events.base_events import ResultPayload
|
|
23
|
+
from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
|
|
24
|
+
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
25
|
+
from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger("griptape_nodes")
|
|
28
|
+
|
|
29
|
+
API_KEY_ENV_VAR = "GT_CLOUD_API_KEY"
|
|
30
|
+
SERVICE = "Griptape"
|
|
31
|
+
|
|
32
|
+
config_manager = ConfigManager()
|
|
33
|
+
secrets_manager = SecretsManager(config_manager)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AgentManager:
|
|
37
|
+
def __init__(self, event_manager: EventManager | None = None) -> None:
|
|
38
|
+
self.conversation_memory = ConversationMemory()
|
|
39
|
+
self.prompt_driver = None
|
|
40
|
+
|
|
41
|
+
if event_manager is not None:
|
|
42
|
+
event_manager.assign_manager_to_request_type(RunAgentRequest, self.on_handle_run_agent_request)
|
|
43
|
+
event_manager.assign_manager_to_request_type(ConfigureAgentRequest, self.on_handle_configure_agent_request)
|
|
44
|
+
event_manager.assign_manager_to_request_type(
|
|
45
|
+
ResetAgentConversationMemoryRequest, self.on_handle_reset_agent_conversation_memory_request
|
|
46
|
+
)
|
|
47
|
+
event_manager.assign_manager_to_request_type(
|
|
48
|
+
GetConversationMemoryRequest, self.on_handle_get_conversation_memory_request
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def _initialize_prompt_driver(self) -> GriptapeCloudPromptDriver:
|
|
52
|
+
api_key = secrets_manager.get_secret(API_KEY_ENV_VAR)
|
|
53
|
+
if not api_key:
|
|
54
|
+
msg = f"Secret '{API_KEY_ENV_VAR}' not found"
|
|
55
|
+
raise ValueError(msg)
|
|
56
|
+
return GriptapeCloudPromptDriver(api_key=api_key)
|
|
57
|
+
|
|
58
|
+
def on_handle_run_agent_request(self, request: RunAgentRequest) -> ResultPayload:
|
|
59
|
+
try:
|
|
60
|
+
if self.prompt_driver is None:
|
|
61
|
+
self.prompt_driver = self._initialize_prompt_driver()
|
|
62
|
+
task_output = PromptTask(
|
|
63
|
+
request.input, prompt_driver=self.prompt_driver, conversation_memory=self.conversation_memory
|
|
64
|
+
).run()
|
|
65
|
+
if isinstance(task_output, ErrorArtifact):
|
|
66
|
+
details = f"Error running agent: {task_output.value}"
|
|
67
|
+
logger.error(details)
|
|
68
|
+
return RunAgentResultFailure(error=task_output.to_json())
|
|
69
|
+
return RunAgentResultSuccess(output=task_output.to_json())
|
|
70
|
+
except Exception as e:
|
|
71
|
+
return RunAgentResultFailure(ErrorArtifact(e).to_json())
|
|
72
|
+
|
|
73
|
+
def on_handle_configure_agent_request(self, request: ConfigureAgentRequest) -> ResultPayload:
|
|
74
|
+
try:
|
|
75
|
+
if self.prompt_driver is None:
|
|
76
|
+
self.prompt_driver = self._initialize_prompt_driver()
|
|
77
|
+
for key, value in request.prompt_driver.items():
|
|
78
|
+
setattr(self.prompt_driver, key, value)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
details = f"Error configuring agent: {e}"
|
|
81
|
+
logger.error(details)
|
|
82
|
+
return ConfigureAgentResultFailure()
|
|
83
|
+
return ConfigureAgentResultSuccess()
|
|
84
|
+
|
|
85
|
+
def on_handle_reset_agent_conversation_memory_request(
|
|
86
|
+
self, _: ResetAgentConversationMemoryRequest
|
|
87
|
+
) -> ResultPayload:
|
|
88
|
+
try:
|
|
89
|
+
self.conversation_memory = ConversationMemory()
|
|
90
|
+
except Exception as e:
|
|
91
|
+
details = f"Error resetting agent conversation memory: {e}"
|
|
92
|
+
logger.error(details)
|
|
93
|
+
return ResetAgentConversationMemoryResultFailure()
|
|
94
|
+
return ResetAgentConversationMemoryResultSuccess()
|
|
95
|
+
|
|
96
|
+
def on_handle_get_conversation_memory_request(self, _: GetConversationMemoryRequest) -> ResultPayload:
|
|
97
|
+
try:
|
|
98
|
+
conversation_memory = self.conversation_memory.runs
|
|
99
|
+
except Exception as e:
|
|
100
|
+
details = f"Error getting conversation memory: {e}"
|
|
101
|
+
logger.error(details)
|
|
102
|
+
return GetConversationMemoryResultFailure()
|
|
103
|
+
return GetConversationMemoryResultSuccess(runs=conversation_memory)
|
|
@@ -456,37 +456,14 @@ class LibraryManager:
|
|
|
456
456
|
|
|
457
457
|
# Install node library dependencies
|
|
458
458
|
try:
|
|
459
|
-
site_packages = None
|
|
460
459
|
if library_data.metadata.dependencies and library_data.metadata.dependencies.pip_dependencies:
|
|
461
460
|
pip_install_flags = library_data.metadata.dependencies.pip_install_flags
|
|
462
461
|
if pip_install_flags is None:
|
|
463
462
|
pip_install_flags = []
|
|
464
463
|
pip_dependencies = library_data.metadata.dependencies.pip_dependencies
|
|
465
464
|
|
|
466
|
-
# Create a virtual environment for the library
|
|
467
|
-
python_version = platform.python_version()
|
|
468
|
-
library_venv_path = (
|
|
469
|
-
xdg_data_home()
|
|
470
|
-
/ "griptape_nodes"
|
|
471
|
-
/ "venvs"
|
|
472
|
-
/ python_version
|
|
473
|
-
/ library_data.name.replace(" ", "_").strip()
|
|
474
|
-
)
|
|
475
|
-
if library_venv_path.exists():
|
|
476
|
-
logger.debug("Virtual environment already exists at %s", library_venv_path)
|
|
477
|
-
else:
|
|
478
|
-
subprocess.run( # noqa: S603
|
|
479
|
-
[sys.executable, "-m", "uv", "venv", library_venv_path, "--python", python_version],
|
|
480
|
-
check=True,
|
|
481
|
-
text=True,
|
|
482
|
-
)
|
|
483
|
-
logger.debug("Created virtual environment at %s", library_venv_path)
|
|
484
|
-
|
|
485
465
|
# Grab the python executable from the virtual environment so that we can pip install there
|
|
486
|
-
|
|
487
|
-
library_venv_python_path = library_venv_path / "Scripts" / "python.exe"
|
|
488
|
-
else:
|
|
489
|
-
library_venv_python_path = library_venv_path / "bin" / "python"
|
|
466
|
+
library_venv_python_path = self._get_library_venv_python_path(library_data.name)
|
|
490
467
|
subprocess.run( # noqa: S603
|
|
491
468
|
[
|
|
492
469
|
sys.executable,
|
|
@@ -502,16 +479,6 @@ class LibraryManager:
|
|
|
502
479
|
check=True,
|
|
503
480
|
text=True,
|
|
504
481
|
)
|
|
505
|
-
# Need to insert into the path so that the library picks up on the venv
|
|
506
|
-
site_packages = str(
|
|
507
|
-
Path(
|
|
508
|
-
sysconfig.get_path(
|
|
509
|
-
"purelib",
|
|
510
|
-
vars={"base": str(library_venv_path), "platbase": str(library_venv_path)},
|
|
511
|
-
)
|
|
512
|
-
)
|
|
513
|
-
)
|
|
514
|
-
sys.path.insert(0, site_packages)
|
|
515
482
|
except subprocess.CalledProcessError as e:
|
|
516
483
|
# Failed to create the library
|
|
517
484
|
self._library_file_path_to_info[file_path] = LibraryManager.LibraryInfo(
|
|
@@ -595,15 +562,26 @@ class LibraryManager:
|
|
|
595
562
|
def register_library_from_requirement_specifier_request(
|
|
596
563
|
self, request: RegisterLibraryFromRequirementSpecifierRequest
|
|
597
564
|
) -> ResultPayload:
|
|
565
|
+
package_name = Requirement(request.requirement_specifier).name
|
|
598
566
|
try:
|
|
599
|
-
|
|
567
|
+
library_python_venv_path = self._get_library_venv_python_path(package_name)
|
|
568
|
+
subprocess.run( # noqa: S603
|
|
569
|
+
[
|
|
570
|
+
uv.find_uv_bin(),
|
|
571
|
+
"pip",
|
|
572
|
+
"install",
|
|
573
|
+
request.requirement_specifier,
|
|
574
|
+
"--python",
|
|
575
|
+
library_python_venv_path,
|
|
576
|
+
],
|
|
577
|
+
check=True,
|
|
578
|
+
text=True,
|
|
579
|
+
)
|
|
600
580
|
except subprocess.CalledProcessError as e:
|
|
601
581
|
details = f"Attempted to install library '{request.requirement_specifier}'. Failed due to {e}"
|
|
602
582
|
logger.error(details)
|
|
603
583
|
return RegisterLibraryFromRequirementSpecifierResultFailure()
|
|
604
584
|
|
|
605
|
-
package_name = Requirement(request.requirement_specifier).name
|
|
606
|
-
|
|
607
585
|
library_path = str(files(package_name).joinpath(request.library_config_name))
|
|
608
586
|
|
|
609
587
|
register_result = GriptapeNodes.handle_request(RegisterLibraryFromFileRequest(file_path=library_path))
|
|
@@ -614,6 +592,41 @@ class LibraryManager:
|
|
|
614
592
|
|
|
615
593
|
return RegisterLibraryFromRequirementSpecifierResultSuccess(library_name=request.requirement_specifier)
|
|
616
594
|
|
|
595
|
+
def _get_library_venv_python_path(self, library_name: str) -> Path:
|
|
596
|
+
# Create a virtual environment for the library
|
|
597
|
+
python_version = platform.python_version()
|
|
598
|
+
library_venv_path = (
|
|
599
|
+
xdg_data_home() / "griptape_nodes" / "venvs" / python_version / library_name.replace(" ", "_").strip()
|
|
600
|
+
)
|
|
601
|
+
if library_venv_path.exists():
|
|
602
|
+
logger.debug("Virtual environment already exists at %s", library_venv_path)
|
|
603
|
+
else:
|
|
604
|
+
subprocess.run( # noqa: S603
|
|
605
|
+
[sys.executable, "-m", "uv", "venv", str(library_venv_path), "--python", python_version],
|
|
606
|
+
check=True,
|
|
607
|
+
text=True,
|
|
608
|
+
)
|
|
609
|
+
logger.debug("Created virtual environment at %s", library_venv_path)
|
|
610
|
+
|
|
611
|
+
# Grab the python executable from the virtual environment so that we can pip install there
|
|
612
|
+
if OSManager.is_windows():
|
|
613
|
+
library_venv_python_path = library_venv_path / "Scripts" / "python.exe"
|
|
614
|
+
else:
|
|
615
|
+
library_venv_python_path = library_venv_path / "bin" / "python"
|
|
616
|
+
|
|
617
|
+
# Need to insert into the path so that the library picks up on the venv
|
|
618
|
+
site_packages = str(
|
|
619
|
+
Path(
|
|
620
|
+
sysconfig.get_path(
|
|
621
|
+
"purelib",
|
|
622
|
+
vars={"base": str(library_venv_path), "platbase": str(library_venv_path)},
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
)
|
|
626
|
+
sys.path.insert(0, site_packages)
|
|
627
|
+
|
|
628
|
+
return library_venv_python_path
|
|
629
|
+
|
|
617
630
|
def unload_library_from_registry_request(self, request: UnloadLibraryFromRegistryRequest) -> ResultPayload:
|
|
618
631
|
try:
|
|
619
632
|
LibraryRegistry.unregister_library(library_name=request.library_name)
|
|
@@ -14,7 +14,7 @@ from griptape_nodes.exe_types.core_types import (
|
|
|
14
14
|
ParameterTypeBuiltin,
|
|
15
15
|
)
|
|
16
16
|
from griptape_nodes.exe_types.flow import ControlFlow
|
|
17
|
-
from griptape_nodes.exe_types.node_types import BaseNode, NodeResolutionState
|
|
17
|
+
from griptape_nodes.exe_types.node_types import BaseNode, EndLoopNode, NodeResolutionState, StartLoopNode
|
|
18
18
|
from griptape_nodes.exe_types.type_validator import TypeValidator
|
|
19
19
|
from griptape_nodes.node_library.library_registry import LibraryNameAndVersion, LibraryRegistry
|
|
20
20
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
@@ -284,7 +284,6 @@ class NodeManager:
|
|
|
284
284
|
details = f"Could not create Node '{final_node_name}' of type '{request.node_type}': {err}"
|
|
285
285
|
logger.error(details)
|
|
286
286
|
return CreateNodeResultFailure()
|
|
287
|
-
|
|
288
287
|
# Add it to the Flow.
|
|
289
288
|
parent_flow.add_node(node)
|
|
290
289
|
|
|
@@ -320,6 +319,33 @@ class NodeManager:
|
|
|
320
319
|
|
|
321
320
|
logger.log(level=log_level, msg=details)
|
|
322
321
|
|
|
322
|
+
if isinstance(node, StartLoopNode) and not request.initial_setup:
|
|
323
|
+
# If it's StartLoop, create an EndLoop and connect it to the StartLoop.
|
|
324
|
+
end_loop = GriptapeNodes.handle_request(
|
|
325
|
+
CreateNodeRequest(
|
|
326
|
+
node_type="ForEachEndNode",
|
|
327
|
+
metadata={
|
|
328
|
+
"position": {"x": node.metadata["position"]["x"] + 650, "y": node.metadata["position"]["y"]}
|
|
329
|
+
},
|
|
330
|
+
override_parent_flow_name=parent_flow_name,
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
if isinstance(end_loop, CreateNodeResultSuccess):
|
|
334
|
+
# Create Loop between output and input to the start node.
|
|
335
|
+
GriptapeNodes.handle_request(
|
|
336
|
+
CreateConnectionRequest(
|
|
337
|
+
source_node_name=node.name,
|
|
338
|
+
source_parameter_name="loop",
|
|
339
|
+
target_node_name=end_loop.node_name,
|
|
340
|
+
target_parameter_name="from_start",
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
end_node = self.get_node_by_name(end_loop.node_name)
|
|
344
|
+
if isinstance(end_node, EndLoopNode):
|
|
345
|
+
# create the connection bt them
|
|
346
|
+
node.end_node = end_node
|
|
347
|
+
end_node.start_node = node
|
|
348
|
+
|
|
323
349
|
return CreateNodeResultSuccess(
|
|
324
350
|
node_name=node.name, node_type=node.__class__.__name__, specific_library_name=request.specific_library_name
|
|
325
351
|
)
|
|
@@ -1723,6 +1749,7 @@ class NodeManager:
|
|
|
1723
1749
|
metadata=copy.deepcopy(node.metadata),
|
|
1724
1750
|
# If it is actively resolving, mark as unresolved.
|
|
1725
1751
|
resolution=node.state.value,
|
|
1752
|
+
initial_setup=True,
|
|
1726
1753
|
)
|
|
1727
1754
|
|
|
1728
1755
|
# We're going to compare this node instance vs. a canonical one. Rez that one up.
|