diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a668f..771d14c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +## [2.1.11] - 2025-15-01 +### Added +- `Griptape Agent Config: Groq Drivers` - A Config node for using Groq (https://groq.groqcloud/). Currently contains both the PromptDriver and Audio Transcription Driver. Note: Groq doesn't currently have an embedding driver, which means in order to use it with off_prompt tools, you must use a separate embedding driver. It's on their list, but haven't heard back as to when it'll be available. +- `Griptape Prompt Driver: Groq` - a default prompt driver for working with Groq (https://groq.com/groqcloud/). You will need a `GROQ_API_KEY` to use this node, but you can get one FREE at: https://console.groq.com/keys +- `Griptape Audio Transcription Driver: Groq` - a node to use Groq to perform Audio Transcriptions. You will need a `GROQ_API_KEY` to use this node, but you can get one FREE at: https://console.groq.com/keys +### Changed +- Griptape Framework updated to `1.1.3` +- `Griptape Embedding Driver: OpenAI Compatible` now has a string for the `embedding_model`. +### Deprecated + +## [2.1.10] - 2025-15-01 ### Added - `GeminiQueryTool` - a patch for the QueryTool: https://github.com/griptape-ai/griptape/issues/1572 ### Changed - `gtComfyAgent` checks to see if the `prompt_driver` is `GooglePromptDriver`. if so, it uses the `GeminiQueryTool` isntead of `QueryTool`. - `QueryTool` now checks to see if the `prompt_driver` is `GooglePromptDriver`. If so, it uses the `GeminiQueryTool` instead of `QueryTool`. -### Deprecated -### Removed ### Fixed - `GooglePromptDriver` can now handle tools that are `off_prompt`. -### Security --> ## [2.1.09] - 2025-10-01 ### Added diff --git a/__init__.py b/__init__.py index 973fccc..803ac5e 100644 --- a/__init__.py +++ b/__init__.py @@ -52,6 +52,7 @@ from .nodes.config.gtUIAzureOpenAiDriversConfig import gtUIAzureOpenAiDriversCon from .nodes.config.gtUICohereDriversConfig import gtUICohereDriversConfig from .nodes.config.gtUIEnvConfig import gtUIEnvConfig from .nodes.config.gtUIGoogleDriversConfig import gtUIGoogleDriversConfig +from .nodes.config.gtUIGroqDriversConfig import gtUIGroqDriversConfig from .nodes.config.gtUIHuggingFaceDriversConfig import gtUIHuggingFaceDriversConfig from .nodes.config.gtUILMStudioDriversConfig import gtUILMStudioDriversConfig from .nodes.config.gtUIOllamaDriversConfig import gtUIOllamaDriversConfig @@ -145,6 +146,12 @@ from .nodes.drivers.gtUIGoogleWebSearchDriver import gtUIGoogleWebSearchDriver from .nodes.drivers.gtUIGriptapeCloudVectorStoreDriver import ( gtUIGriptapeCloudVectorStoreDriver, ) +from .nodes.drivers.gtUIGroqAudioTranscriptionDriver import ( + gtUIGroqAudioTranscriptionDriver, +) + +# - Groq +from .nodes.drivers.gtUIGroqChatPromptDriver import gtUIGroqChatPromptDriver # - HuggingFace from .nodes.drivers.gtUIHuggingFaceHubEmbeddingDriver import ( @@ -329,6 +336,7 @@ NODE_CLASS_MAPPINGS = { "Griptape Agent Config: Azure OpenAI Drivers": gtUIAzureOpenAiDriversConfig, "Griptape Agent Config: Cohere Drivers": gtUICohereDriversConfig, "Griptape Agent Config: Google Drivers": gtUIGoogleDriversConfig, + "Griptape Agent Config: Groq Drivers": gtUIGroqDriversConfig, "Griptape Agent Config: HuggingFace Drivers": gtUIHuggingFaceDriversConfig, "Griptape Agent Config: LM Studio Drivers": gtUILMStudioDriversConfig, "Griptape Agent Config: Ollama Drivers": gtUIOllamaDriversConfig, @@ -350,6 +358,7 @@ NODE_CLASS_MAPPINGS = { "Griptape Prompt Driver: Anthropic": gtUIAnthropicPromptDriver, "Griptape Prompt Driver: Azure OpenAI": gtUIAzureOpenAiChatPromptDriver, "Griptape Prompt Driver: Cohere": gtUICoherePromptDriver, + "Griptape Prompt Driver: Groq": gtUIGroqChatPromptDriver, "Griptape Prompt Driver: Google": gtUIGooglePromptDriver, "Griptape Prompt Driver: HuggingFace": gtUIHuggingFaceHubPromptDriver, "Griptape Prompt Driver: LM Studio": gtUILMStudioChatPromptDriver, @@ -393,6 +402,7 @@ NODE_CLASS_MAPPINGS = { "Griptape Text To Speech Driver: ElevenLabs": gtUIElevenLabsTextToSpeechDriver, "Griptape Text To Speech Driver: OpenAI": gtUIOpenAiTextToSpeechDriver, # AUDIO DRIVERS + "Griptape Audio Transcription Driver: Groq": gtUIGroqAudioTranscriptionDriver, "Griptape Audio Transcription Driver: OpenAI": gtUIOpenAiAudioTranscriptionDriver, # WEBSEARCH DRIVERS "Griptape WebSearch Driver: DuckDuckGo": gtUIDuckDuckGoWebSearchDriver, diff --git a/examples/run_python_nodes.png b/docs/images/run_python_nodes.png similarity index 100% rename from examples/run_python_nodes.png rename to docs/images/run_python_nodes.png diff --git a/examples/README.md b/examples/README.md index b66353f..97a046b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -65,7 +65,7 @@ Download: [retrieval_augmented_generation.json](retrieval_augmented_generation.j ## Sorting a CSV - by using generic Python! With the `Griptape Code: Run Python` node, you can use generic Python code to work with data. This node requires turning on the `Enable Griptape Code: Run Python Nodes` setting to be enabled. -![alt text](run_python_nodes.png) +![alt text](../docs/images/run_python_nodes.png) ![Run Python](sort_list_with_python_code.png) Download: [sort_list_with_python_code.png](sort_list_with_python_code.png) \ No newline at end of file diff --git a/examples/sort_list_with_python_code.json b/examples/sort_list_with_python_code.json new file mode 100644 index 0000000..94ea6ad --- /dev/null +++ b/examples/sort_list_with_python_code.json @@ -0,0 +1,336 @@ +{ + "last_node_id": 8, + "last_link_id": 8, + "nodes": [ + { + "id": 1, + "type": "Griptape Code: Run Python", + "pos": [ + -175, + 100 + ], + "size": [ + 950, + 550 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "agent", + "type": "AGENT", + "link": null, + "shape": 7 + }, + { + "name": "input", + "type": "STRING", + "link": 3, + "widget": { + "name": "input" + } + } + ], + "outputs": [ + { + "name": "OUTPUT", + "type": "STRING", + "links": [ + 8 + ], + "slot_index": 0 + }, + { + "name": "AGENT", + "type": "AGENT", + "links": [], + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "Griptape Code: Run Python" + }, + "widgets_values": [ + "This text will be passed as `input` to the code.", + "Sort rows based on column from CSV", + "# Sort rows based on Name\n\nimport csv\nfrom io import StringIO\n\ndef sort_by_column(csv_data, column_name):\n reader = csv.DictReader(StringIO(csv_data))\n sorted_rows = sorted(reader, key=lambda row: row[column_name])\n return \"\\n\".join([\",\".join(sorted_rows[0].keys())] + [\",\".join(row.values()) for row in sorted_rows])\n\n# Example: Sort rows by \"Name\"\noutput = sort_by_column(input, \"Name\")" + ] + }, + { + "id": 3, + "type": "Griptape Create: Agent", + "pos": [ + -1500, + 225 + ], + "size": [ + 525, + 225 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "agent", + "type": "AGENT", + "link": null, + "shape": 7 + }, + { + "name": "config", + "type": "CONFIG", + "link": null, + "shape": 7 + }, + { + "name": "tools", + "type": "TOOL_LIST", + "link": null, + "shape": 7 + }, + { + "name": "rulesets", + "type": "RULESET", + "link": 5, + "shape": 7 + }, + { + "name": "input_string", + "type": "STRING", + "link": null, + "widget": { + "name": "input_string" + }, + "shape": 7 + } + ], + "outputs": [ + { + "name": "OUTPUT", + "type": "STRING", + "links": [ + 2 + ], + "slot_index": 0 + }, + { + "name": "AGENT", + "type": "AGENT", + "links": null + } + ], + "properties": { + "Node name for S&R": "Griptape Create: Agent" + }, + "widgets_values": [ + "", + "Create a csv of 20 people. Include Name, Age, Country, and Interest." + ], + "color": "#323", + "bgcolor": "#535" + }, + { + "id": 4, + "type": "Griptape Display: Text", + "pos": [ + -925, + -25 + ], + "size": [ + 500, + 850 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "INPUT", + "type": "STRING", + "link": 2, + "widget": { + "name": "INPUT" + }, + "shape": 7 + } + ], + "outputs": [ + { + "name": "OUTPUT", + "type": "STRING", + "links": [ + 3 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "Griptape Display: Text" + }, + "widgets_values": [ + "", + "Name,Age,Country,Interest\nAlice Smith,28,USA,Photography\nBob Johnson,34,Canada,Cycling\nCharlie Brown,22,UK,Reading\nDiana Prince,30,Australia,Traveling\nEthan Hunt,26,USA,Movies\nFiona Green,29,New Zealand,Gardening\nGeorge White,31,UK,Music\nHannah Lee,24,South Korea,Dancing\nIan Wright,35,Canada,Technology\nJulia Roberts,27,USA,Cooking\nKevin Bacon,33,Australia,Sports\nLaura Croft,25,UK,Art\nMichael Jordan,40,USA,Basketball\nNina Simone,32,France,Singing\nOscar Wilde,29,Ireland,Writing\nPaula Abdul,36,USA,Dance\nQuincy Jones,38,USA,Music Production\nRachel Green,30,USA,Fashion\nSteve Jobs,45,USA,Entrepreneurship\nTina Turner,81,USA,Performing\nUma Thurman,53,USA,Acting" + ], + "color": "#222", + "bgcolor": "#000" + }, + { + "id": 5, + "type": "Griptape Display: Text", + "pos": [ + 850, + 0 + ], + "size": [ + 575, + 625 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "INPUT", + "type": "STRING", + "link": 8, + "widget": { + "name": "INPUT" + }, + "shape": 7 + } + ], + "outputs": [ + { + "name": "OUTPUT", + "type": "STRING", + "links": [], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "Griptape Display: Text" + }, + "widgets_values": [ + "", + "Name,Age,Country,Interest\nAlice Smith,28,USA,Photography\nBob Johnson,34,Canada,Cycling\nCharlie Brown,22,UK,Reading\nDiana Prince,30,Australia,Traveling\nEthan Hunt,26,USA,Movies\nFiona Green,29,New Zealand,Gardening\nGeorge White,31,UK,Music\nHannah Lee,24,South Korea,Dancing\nIan Wright,35,Canada,Technology\nJulia Roberts,27,USA,Cooking\nKevin Bacon,33,Australia,Sports\nLaura Croft,25,UK,Art\nMichael Jordan,40,USA,Basketball\nNina Simone,32,France,Singing\nOscar Wilde,29,Ireland,Writing\nPaula Abdul,36,USA,Dance\nQuincy Jones,38,USA,Music Production\nRachel Green,30,USA,Fashion\nSteve Jobs,45,USA,Entrepreneurship\nTina Turner,81,USA,Performing\nUma Thurman,53,USA,Acting" + ], + "color": "#222", + "bgcolor": "#000" + }, + { + "id": 6, + "type": "Griptape Create: Rules", + "pos": [ + -1500, + -25 + ], + "size": [ + 525, + 200 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "RULES", + "type": "RULESET", + "links": [ + 5 + ] + }, + { + "name": "NAME", + "type": "STRING", + "links": null + } + ], + "properties": { + "Node name for S&R": "Griptape Create: Rules" + }, + "widgets_values": [ + "My rules", + "Only output the requested information.\nNever wrap in markdown." + ], + "color": "#233", + "bgcolor": "#355" + } + ], + "links": [ + [ + 2, + 3, + 0, + 4, + 0, + "STRING" + ], + [ + 3, + 4, + 0, + 1, + 1, + "STRING" + ], + [ + 5, + 6, + 0, + 3, + 3, + "RULESET" + ], + [ + 8, + 1, + 0, + 5, + 0, + "STRING" + ] + ], + "groups": [ + { + "id": 1, + "title": "Create Agent", + "bounding": [ + -1550, + -175, + 1150, + 1150 + ], + "color": "#3f789e", + "font_size": 22, + "flags": {} + }, + { + "id": 2, + "title": "Sort List", + "bounding": [ + -225, + -150, + 1725, + 1000 + ], + "color": "#8A8", + "font_size": 22, + "flags": {} + } + ], + "config": {}, + "extra": { + "ds": { + "scale": 0.6209213230591554, + "offset": [ + 1601.6863942999978, + 537.0984377999998 + ] + }, + "ue_links": [] + }, + "version": 0.4 +} \ No newline at end of file diff --git a/examples/sort_list_with_python_code.png b/examples/sort_list_with_python_code.png index 4f34aac..e4cb44a 100644 Binary files a/examples/sort_list_with_python_code.png and b/examples/sort_list_with_python_code.png differ diff --git a/nodes/agent/BaseAgent.py b/nodes/agent/BaseAgent.py index 4ce74a0..cbd2691 100644 --- a/nodes/agent/BaseAgent.py +++ b/nodes/agent/BaseAgent.py @@ -153,9 +153,6 @@ class BaseAgent: tools = kwargs.get("tools", []) rulesets = kwargs.get("rulesets", []) input_string = kwargs.get("input_string", None) - # print(f"STRING: {STRING=}") - # print(f"input_string: {input_string=}") - # Defaults.drivers_config = DriversConfig() create_dict = {} # Configuration if config: @@ -183,6 +180,8 @@ class BaseAgent: create_dict["task_memory"] = agent.task_memory try: + if create_dict["rulesets"] == [None]: + create_dict["rulesets"] = [] # Now create the agent self.agent = gtComfyAgent(**create_dict) diff --git a/nodes/config/gtUIGroqDriversConfig.py b/nodes/config/gtUIGroqDriversConfig.py new file mode 100644 index 0000000..f4412cb --- /dev/null +++ b/nodes/config/gtUIGroqDriversConfig.py @@ -0,0 +1,77 @@ +# pyright: reportMissingImports=false +import logging + +from comfy_execution.graph import ExecutionBlocker +from griptape.configs import Defaults +from griptape.configs.drivers import ( + DriversConfig, +) + +# StructureGlobalDriversConfig, +from griptape.drivers import OpenAiAudioTranscriptionDriver, OpenAiChatPromptDriver + +from ..drivers.gtUIGroqAudioTranscriptionDriver import gtUIGroqAudioTranscriptionDriver +from ..drivers.gtUIGroqChatPromptDriver import gtUIGroqChatPromptDriver +from .gtUIBaseDriversConfig import ( + add_optional_inputs, + add_required_inputs, + gtUIBaseDriversConfig, +) + +drivers = [ + ("prompt", gtUIGroqChatPromptDriver), + ("audio_transcription", gtUIGroqAudioTranscriptionDriver), +] + + +class gtUIGroqDriversConfig(gtUIBaseDriversConfig): + """ + The Griptape LM Studio Structure Config + """ + + DESCRIPTION = "Groq Driver Configs. Groq requires a GROQ_API_KEY available at https://console.groq.com" + + @classmethod + def INPUT_TYPES(cls): + inputs = super().INPUT_TYPES() + + inputs["optional"] = {} + + inputs = add_required_inputs(inputs, drivers) + inputs = add_optional_inputs(inputs, drivers) + + return inputs + + def create(self, **kwargs): + drivers_config_params = {} + + try: + # Create instances of the driver classes + prompt_driver_builder = gtUIGroqChatPromptDriver() + audio_transcription_driver_builder = gtUIGroqAudioTranscriptionDriver() + + # Build parameters for drivers + prompt_driver_params = prompt_driver_builder.build_params(**kwargs) + audio_transcription_driver_params = ( + audio_transcription_driver_builder.build_params(**kwargs) + ) + if "model" not in prompt_driver_params: + custom_config = ExecutionBlocker( + "Please provide a model for the prompt driver." + ) + return (custom_config,) + + # Create Driver Configs + drivers_config_params["prompt_driver"] = OpenAiChatPromptDriver( + **prompt_driver_params + ) + drivers_config_params["vector_store_driver"] = ( + OpenAiAudioTranscriptionDriver(**audio_transcription_driver_params) + ) + Defaults.drivers_config = DriversConfig(**drivers_config_params) + custom_config = Defaults.drivers_config + except Exception as e: + custom_config = ExecutionBlocker(f"{e}") + logging.error(e) + + return (custom_config,) diff --git a/nodes/drivers/gtUIGroqAudioTranscriptionDriver.py b/nodes/drivers/gtUIGroqAudioTranscriptionDriver.py new file mode 100644 index 0000000..83d8270 --- /dev/null +++ b/nodes/drivers/gtUIGroqAudioTranscriptionDriver.py @@ -0,0 +1,58 @@ +from griptape.drivers import OpenAiAudioTranscriptionDriver + +from .gtUIBaseAudioTranscriptionDriver import gtUIBaseAudioTranscriptionDriver + +models = ["whisper-large-v3-turbo", "distil-whisper-large-v3-en", "whisper-large-v3"] +base_url = "https://api.groq.com/openai/v1/" +DEFAULT_MODEL = "" +if len(models) > 0: + DEFAULT_MODEL = models[0] + +DEFAULT_API_KEY = "GROQ_API_KEY" + + +class gtUIGroqAudioTranscriptionDriver(gtUIBaseAudioTranscriptionDriver): + DESCRIPTION = "Groq Audio Transcription Driver" + + @classmethod + def INPUT_TYPES(cls): + inputs = super().INPUT_TYPES() + + inputs["optional"].update( + { + "audio_transcription_model": ( + models, + { + "default": DEFAULT_MODEL, + "tooltip": "Select the audio transcription model to use.", + }, + ), + "api_key_env_var": ( + "STRING", + { + "default": DEFAULT_API_KEY, + "tooltip": "Enter the name of the environment variable that contains the API key, not the API key itself.", + }, + ), + }, + ) + + return inputs + + def build_params(self, **kwargs): + model = kwargs.get("audio_transcription_model", DEFAULT_MODEL) + api_key = self.getenv(kwargs.get("api_key_env_var", DEFAULT_API_KEY)) + + params = {} + + if model: + params["model"] = model + if api_key: + params["api_key"] = api_key + params["base_url"] = base_url + return params + + def create(self, **kwargs): + params = self.build_params(**kwargs) + driver = OpenAiAudioTranscriptionDriver(**params) + return (driver,) diff --git a/nodes/drivers/gtUIGroqChatPromptDriver.py b/nodes/drivers/gtUIGroqChatPromptDriver.py new file mode 100644 index 0000000..7be0ffa --- /dev/null +++ b/nodes/drivers/gtUIGroqChatPromptDriver.py @@ -0,0 +1,86 @@ +# pyright: reportMissingImports=false +from comfy_execution.graph import ExecutionBlocker +from griptape.drivers import OpenAiChatPromptDriver + +from .gtUIOpenAiCompatibleChatPromptDriver import gtUIOpenAiCompatibleChatPromptDriver + +default_base_url = "https://api.groq.com/openai/v1" +DEFAULT_API_KEY_ENV = "GROQ_API_KEY" + + +class gtUIGroqChatPromptDriver(gtUIOpenAiCompatibleChatPromptDriver): + @classmethod + def INPUT_TYPES(cls): + inputs = super().INPUT_TYPES() + + del inputs["optional"]["base_url"] + inputs["optional"].update( + { + "model": ( + "STRING", + { + "default": "llama-3.3-70b-versatile", + "tooltip": "The model to use for the chat prompt.", + }, + ), + "use_native_tools": ( + "BOOLEAN", + { + "default": True, + "tooltip": "Whether to use native tools.", + "label_on": "True (LLM-native tool calling)", + "label_off": "False (Griptape tool calling)", + }, + ), + "api_key_env_var": ( + "STRING", + { + "default": DEFAULT_API_KEY_ENV, + "tooltip": "API key variable for authentication.", + }, + ), + } + ) + + return inputs + + FUNCTION = "create" + + def build_params(self, **kwargs): + model = kwargs.get("model", None) + response_format = kwargs.get("response_format", None) + api_key = self.getenv(kwargs.get("api_key_env_var", DEFAULT_API_KEY_ENV)) + + temperature = kwargs.get("temperature", None) + max_attempts = kwargs.get("max_attempts_on_fail", None) + use_native_tools = kwargs.get("use_native_tools", False) + max_tokens = kwargs.get("max_tokens", None) + + params = { + "model": model, + "base_url": default_base_url, + "api_key": api_key, + "temperature": temperature, + "use_native_tools": use_native_tools, + "max_attempts": max_attempts, + } + if response_format == "json_object": + params["response_format"] = {"type": response_format} + if max_tokens > 0: + params["max_tokens"] = max_tokens + + return params + + def create(self, **kwargs): + params = self.build_params(**kwargs) + + print(params) + if not params.get("model"): + driver = ExecutionBlocker("Model is required.") + return (driver,) + try: + driver = OpenAiChatPromptDriver(**params) + return (driver,) + except Exception as e: + driver = ExecutionBlocker(f"Error creating driver: {e}") + return (driver,) diff --git a/nodes/drivers/gtUIOpenAiCompatibleEmbeddingDriver.py b/nodes/drivers/gtUIOpenAiCompatibleEmbeddingDriver.py index fcca18d..7a62884 100644 --- a/nodes/drivers/gtUIOpenAiCompatibleEmbeddingDriver.py +++ b/nodes/drivers/gtUIOpenAiCompatibleEmbeddingDriver.py @@ -19,7 +19,7 @@ class gtUIOpenAiCompatibleEmbeddingDriver(gtUIBaseEmbeddingDriver): inputs["optional"].update( { "embedding_model": ( - models, + "STRING", { "default": models[0], "tooltip": "Select the embedding model to use.", diff --git a/nodes/tasks/gtUIImageQueryTask.py b/nodes/tasks/gtUIImageQueryTask.py index 5b12974..6d4c4a0 100644 --- a/nodes/tasks/gtUIImageQueryTask.py +++ b/nodes/tasks/gtUIImageQueryTask.py @@ -17,6 +17,12 @@ default_prompt = "{{ input_string }}" class gtUIImageQueryTask(gtUIBaseImageTask): DESCRIPTION = "Query an image for a detailed description." + def _is_grog(self, prompt_driver): + if hasattr(prompt_driver, "base_url") and prompt_driver.base_url: + if "groq" in prompt_driver.base_url: + return True + return False + def run(self, **kwargs): STRING = kwargs.get("STRING") image = kwargs.get("image") @@ -38,6 +44,23 @@ class gtUIImageQueryTask(gtUIBaseImageTask): ): prompt_text = "Describe this image" + # If the driver is Groq, rulesets _must_ be empty + rulesets = [] + tmp_agent_rulesets = [] + tmp_task_rulesets = [] + + if self._is_grog(prompt_driver): + if len(agent.rulesets) > 0: + print( + "[Griptape Run: Image Description] Warning: Rulesets are not supported with Groq. Removing temporarily while getting Image Description." + ) + tmp_agent_rulesets = agent.rulesets + tmp_task_rulesets = agent.tasks[0].rulesets + agent._rulesets = [] + agent.tasks[0]._rulesets = [] + else: + rulesets = agent.rulesets + # Load each of the images image_artifacts = [] for base64Image in images: try: @@ -46,15 +69,16 @@ class gtUIImageQueryTask(gtUIBaseImageTask): ) except Exception as e: raise RuntimeError(f"Couldn't load image {e}") - # Depending on the model, we might need to use a workflow instead of a simple prompt - rulesets = agent.rulesets + tasks = [] + # Depending on the model, we might need to use a workflow instead of a simple prompt if len(image_artifacts) > 2: task_args = {} if agent.drivers_config.prompt_driver: task_args["prompt_driver"] = prompt_driver if len(rulesets) > 0: - task_args["rulesets"] = rulesets + if "groq" not in prompt_driver.base_url: + task_args["rulesets"] = rulesets end_task = PromptTask( "Concatenate just the output values of the tasks, separated by two newlines: {{ parent_outputs }}", id="END", @@ -68,6 +92,11 @@ class gtUIImageQueryTask(gtUIBaseImageTask): result = workflow.run() else: result = agent.run([prompt_text, *image_artifacts]) + + # Reset the rulesets + if self._is_grog(prompt_driver): + agent._rulesets = tmp_agent_rulesets + agent.tasks[0]._rulesets = tmp_task_rulesets output = result.output_task.output.value else: output = "No image provided" diff --git a/nodes/utilities.py b/nodes/utilities.py index 259ba48..b118237 100644 --- a/nodes/utilities.py +++ b/nodes/utilities.py @@ -2,11 +2,14 @@ # pyright: reportOptionalMemberAccess=false import base64 +import os import re +import uuid from io import BytesIO from typing import Optional from urllib.parse import urlparse, urlunparse +import folder_paths import numpy as np import requests import torch @@ -155,6 +158,19 @@ def load_audio_from_artifact(audio_artifact): return audio_output +def save_images_to_disk(images): + image_paths = [] + output_dir = folder_paths.get_temp_directory() + + for batch_number, image in enumerate(images): + i = 255.0 * image.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + filepath = os.path.join(output_dir, uuid.uuid4().hex + ".png") + img.save(filepath) + image_paths.append(filepath) + return image_paths + + # return base64_images def convert_tensor_to_base_64(image): if not isinstance(image, torch.Tensor): diff --git a/pyproject.toml b/pyproject.toml index 98f21dc..8834231 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "comfyui-griptape" -version = "2.1.10" +version = "2.1.11" description = "Griptape LLM(Large Language Model) Nodes for ComfyUI." authors = ["Jason Schleifer "] readme = "README.md" @@ -9,9 +9,9 @@ readme = "README.md" [project] name = "comfyui-griptape" description = "Griptape LLM(Large Language Model) Nodes for ComfyUI." -version = "2.1.10" +version = "2.1.11" license = {file = "LICENSE"} -dependencies = ["attrs>=24.3.0,<25.0.0", "openai>=1.58.1,<2.0.0", "griptape[all]>=1.1.0", "python-dotenv", "poetry==1.8.5", "griptape-black-forest @ git+https://github.com/griptape-ai/griptape-black-forest.git"] +dependencies = ["attrs>=24.3.0,<25.0.0", "openai>=1.58.1,<2.0.0", "griptape[all]>=1.1.3", "python-dotenv", "poetry==1.8.5", "griptape-black-forest @ git+https://github.com/griptape-ai/griptape-black-forest.git"] [project.urls] Repository = "https://github.com/griptape-ai/ComfyUI-Griptape" @@ -21,7 +21,7 @@ Repository = "https://github.com/griptape-ai/ComfyUI-Griptape" python = "^3.11" python-dotenv = "^1.0.1" attrs = "^24.3.0" -griptape = { version = "^1.1.0", extras = ["all"]} +griptape = { version = "^1.1.3", extras = ["all"]} griptape-black-forest = {git = "https://github.com/griptape-ai/griptape-black-forest.git"} [tool.poetry.group.dev] diff --git a/requirements.txt b/requirements.txt index 0a0b0a4..e530916 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs>=24.3.0 openai>=1.58.1 -griptape[all]==1.1.0 +griptape[all]==1.1.3 python-dotenv poetry==1.8.5 git+https://github.com/griptape-ai/griptape-black-forest.git \ No newline at end of file