griptape-nodes 0.36.2__py3-none-any.whl → 0.37.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.
@@ -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
- if OSManager.is_windows():
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
- subprocess.run([uv.find_uv_bin(), "pip", "install", request.requirement_specifier], check=True, text=True) # noqa: S603
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.