Merge pull request #50 from griptape-ai/mergeInputs

MergeInputs
This commit is contained in:
Jason Schleifer
2024-07-01 19:45:40 -07:00
committed by GitHub
9 changed files with 150 additions and 184 deletions
+12 -4
View File
@@ -2,14 +2,16 @@
This repo creates a series of nodes that enable you to utilize the [Griptape Python Framework](https://github.com/griptape-ai/griptape/) with [ComfyUI](https://github.com/comfyanonymous/ComfyUI), integrating LLMs (Large Language Models) and AI into your workflow.
This example creates two [Griptape Agents](https://docs.griptape.ai/stable/griptape-framework/structures/agents/) - a Writer and an Editor. The writer has a [WebScraper](https://docs.griptape.ai/stable/griptape-tools/official-tools/web-scraper/) tool and gets a description of a website. It then generates blog topic ideas and passes them to_ the Editor Agent. The editor chooses the top 3 topics, and generates outlines and next actions for the next two. A third Agent generates thumbnail images for each blog post using a [Prompt Image Generation Task](https://docs.griptape.ai/stable/griptape-framework/structures/tasks/#prompt-image-generation-task) based on the outlines.
### Instructions and tutorials
![Writer and Editor agents](docs/images/writere_editor_agents.png)
Watch the trailer and all the instructional videos on our [YouTube Playlist](https://www.youtube.com/playlist?list=PLZRzNKLLiEyeK9VN-i53sUU1v5vBLl-nG).
[![Watch the Trailer on YouTube](docs/images/playlist.png)](https://www.youtube.com/playlist?list=PLZRzNKLLiEyeK9VN-i53sUU1v5vBLl-nG)
The repo currently has a subset of Griptape nodes, with more to come soon. Current nodes can:
* Create [Agents](https://docs.griptape.ai/stable/griptape-framework/structures/agents/) using these models:
* Local - via Ollama
* Local - via **Ollama** and **LM Studio**
* Llama 3
* Mistral
* etc..
@@ -58,10 +60,16 @@ In this example, we're using three `Image Description` nodes to describe the giv
4. Griptape Tools - Featuring Task Memory and Off-Prompt: https://youtu.be/TvEbv0vTZ5Q
5. Griptape Rulesets, and Image Creation: https://youtu.be/DK16ouQ_vSs
6. Image Generation with multiple drivers: https://youtu.be/Y4vxJmAZcho
7. Image Description, Parallel Image Description: https://youtu.be/OgYKIluSWWs?si=JUNxhvGohYM0YQaK
8. Audio Transcription: https://youtu.be/P4GVmm122B0?si=24b9c4v1LWk_n80T
9. Using Ollama as a Configuration Driver: https://youtu.be/jIq_TL5xmX0?si=ilfomN6Ka1G4hIEp
10. Combining Rulesets: https://youtu.be/3LDdiLwexp8?si=Oeb6ApEUTqIz6J6O
11. Integrating Text: https://youtu.be/_TVr2zZORnA?si=c6tN4pCEE3Qp0sBI
12. New Nodes & Quality of life improvements: https://youtu.be/M2YBxCfyPVo?si=pj3AFAhl2Tjpd_hw
13. Merge Input Data: https://youtu.be/wQ9lKaGWmZo?si=FgboU5iUg82pXRkC
## Installation
#### 1. ComfyUI
Install [ComfyUI](https://github.com/comfyanonymous/ComfyUI) using the [instructions](https://github.com/comfyanonymous/ComfyUI?tab=readme-ov-file#installing) for your particular operating system.
Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

+90
View File
@@ -600,6 +600,96 @@ app.registerExtension({
this?.graph?.setDirtyCanvas(true, true);
};
};
// Combine: Merge Texts
if ( nodeData.name === "Griptape Combine: Merge Texts" ||
nodeData.name === "Griptape Combine: Merge Inputs" ||
nodeData.name === "Griptape Combine: Rules List" ||
nodeData.name === "Griptape Combine: Tool List") {
// Set the base name of the input node
var input_name = "input_";
switch (nodeData.name) {
case 'Griptape Combine: Rules List':
input_name = "rules_";
break;
case 'Griptape Combine: Tool List':
input_name = "tool_";
break;
}
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
if(!link_info)
return;
if(type==1) {
const node = app.graph.getNodeById(link_info.origin_id);
let origin_type = node.outputs[link_info.origin_slot].type;
if(origin_type == '*') {
this.disconnectInput(link_info.target_slot);
}
for(let i in this.inputs) {
let input_i = this.inputs[i];
for(let i in this.inputs) {
let input_i = this.inputs[i];
if(input_i.name != 'select' && input_i.name != 'sel_mode')
input_i.type = origin_type;
}
this.outputs[0].type = origin_type;
this.outputs[0].label = origin_type;
this.outputs[0].name = origin_type;
}
}
let select_slot = this.inputs.find(x => x.name == "select");
let mode_slot = this.inputs.find(x => x.name == "sel_mode");
let converted_count = 0;
converted_count += select_slot?1:0;
converted_count += mode_slot?1:0;
if (!connected && (this.inputs.length > 1+converted_count)) {
const stackTrace = new Error().stack;
if(
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device
!stackTrace.includes('LGraphNode.connect') && // for mouse device
!stackTrace.includes('loadGraphData') &&
this.inputs[index].name != 'select') {
this.removeInput(index);
}
}
let slot_i = 1;
for (let i = 0; i < this.inputs.length; i++) {
let input_i = this.inputs[i];
if(input_i.name != 'select'&& input_i.name != 'sel_mode') {
input_i.name = `${input_name}${slot_i}`
slot_i++;
}
}
let last_slot = this.inputs[this.inputs.length - 1];
if (
(last_slot.name == 'select' && last_slot.name != 'sel_mode' && this.inputs[this.inputs.length - 2].link != undefined)
|| (last_slot.name != 'select' && last_slot.name != 'sel_mode' && last_slot.link != undefined)) {
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
}
if(this.widgets) {
this.widgets[0].options.max = select_slot?this.inputs.length-1:this.inputs.length;
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0)
this.widgets[0].value = 1;
}
}
}
}
});
+3 -4
View File
@@ -16,7 +16,6 @@ class BaseAgent:
def __init__(self):
self.default_prompt = default_prompt
self.agent = gtComfyAgent()
# self.agent.set_default_config()
@classmethod
def INPUT_TYPES(s):
@@ -41,13 +40,12 @@ class BaseAgent:
"STRING",
{
"forceInput": True,
"dynamicPrompts": True,
},
),
"STRING": (
"STRING",
{
"multiline": True,
},
{"multiline": True, "dynamicPrompts": True},
),
},
}
@@ -115,6 +113,7 @@ class BaseAgent:
self.agent.tools = tools
if len(rulesets) > 0:
self.agent.rulesets = rulesets
print(self.agent.rulesets)
# Warn for models
model, simple_model = self.agent.model_check()
+9 -54
View File
@@ -1,74 +1,29 @@
from griptape.structures import Agent
from griptape.tasks import PromptTask
from griptape.artifacts import BaseArtifact
class gtUIBaseTask:
class gtUIBaseLoader:
def __init__(self):
pass
DESCRIPTION = "Run a PromptTask."
DESCRIPTION = "Loader"
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"STRING": (
"STRING",
{
"multiline": True,
},
),
},
"optional": {
"input_string": (
"STRING",
{
"forceInput": True,
},
),
"agent": ("AGENT",),
},
"required": {},
"optional": {},
}
RETURN_TYPES = (
"STRING",
"AGENT",
)
RETURN_NAMES = (
"OUTPUT",
"AGENT",
)
RETURN_TYPES = ("ARTIFACT",)
RETURN_NAMES = ("OUTPUT",)
FUNCTION = "run"
OUTPUT_NODE = True
CATEGORY = "Griptape/Agent Tasks"
def get_prompt_text(self, STRING, input_string):
# Get the prompt text
if not input_string:
prompt_text = STRING
else:
prompt_text = STRING + "\n\n" + input_string
return prompt_text
def run(
self,
STRING,
input_string=None,
agent=None,
):
if not agent:
agent = Agent()
prompt_text = self.get_prompt_text(STRING, input_string)
try:
agent.add_task(PromptTask(prompt_text))
except Exception as e:
print(e)
result = agent.run()
return (
result.output_task.output.value,
agent,
)
artifact = BaseArtifact()
return (artifact,)
+2 -6
View File
@@ -15,17 +15,13 @@ class gtUIBaseTask:
"required": {
"STRING": (
"STRING",
{
"multiline": True,
},
{"multiline": True, "dynamicPrompts": True},
),
},
"optional": {
"input_string": (
"STRING",
{
"forceInput": True,
},
{"forceInput": True, "dynamicPrompts": True},
),
"agent": ("AGENT",),
},
+29 -115
View File
@@ -17,26 +17,6 @@ class MergeTexts:
"STRING",
{"multiline": False, "default": "", "forceInput": True},
),
"input_2": (
"STRING",
{"multiline": False, "default": "", "forceInput": True},
),
"input_3": (
"STRING",
{"multiline": False, "default": "", "forceInput": True},
),
"input_4": (
"STRING",
{"multiline": False, "default": "", "forceInput": True},
),
"input_5": (
"STRING",
{"multiline": False, "default": "", "forceInput": True},
),
"input_6": (
"STRING",
{"multiline": False, "default": "", "forceInput": True},
),
},
}
@@ -48,23 +28,15 @@ class MergeTexts:
def create(
self,
input_1="",
input_2="",
input_3="",
input_4="",
input_5="",
input_6="",
**kwargs,
):
# Join strings by newline
concatenated_string = "\n\n".join(
[
string
for string in [input_1, input_2, input_3, input_4, input_5, input_6]
if not string == ""
],
)
merged_text = ""
return (concatenated_string,)
inputs = [value for value in kwargs.values()]
for input in inputs:
merged_text += "\n\n" + input
return (merged_text,)
class gtUIMergeInputs:
@@ -83,11 +55,6 @@ class gtUIMergeInputs:
"required": {},
"optional": {
"input_1": ("*",),
"input_2": ("*",),
"input_3": ("*",),
"input_4": ("*",),
"input_5": ("*",),
"input_6": ("*",),
},
}
@@ -99,30 +66,13 @@ class gtUIMergeInputs:
def create(
self,
input_1="",
input_2="",
input_3="",
input_4="",
input_5="",
input_6="",
**kwargs,
):
# Join strings by newline
concatenated_string = "\n\n".join(
[
string
for string in [
str(input_1),
str(input_2),
str(input_3),
str(input_4),
str(input_5),
str(input_6),
]
if not string == ""
],
)
return (concatenated_string,)
concatenated_value = ""
inputs = [value for value in kwargs.values()]
for input in inputs:
concatenated_value += "\n\n" + str(input)
return (concatenated_value,)
class RulesList:
@@ -140,11 +90,6 @@ class RulesList:
return {
"optional": {
"rules_1": ("RULESET",),
"rules_2": ("RULESET",),
"rules_3": ("RULESET",),
"rules_4": ("RULESET",),
"rules_5": ("RULESET",),
"rules_6": ("RULESET",),
}
}
@@ -154,27 +99,15 @@ class RulesList:
CATEGORY = "Griptape/Agent Rules"
def create(
self,
rules_1=None,
rules_2=None,
rules_3=None,
rules_4=None,
rules_5=None,
rules_6=None,
):
rule_list = [
rule[0]
for rule in [
rules_1,
rules_2,
rules_3,
rules_4,
rules_5,
rules_6,
]
if rule is not None
]
def create(self, **kwargs):
# Clear the rule_list
rule_list = []
rules = [value for value in kwargs.values()]
if len(rules) > 0:
for rule in rules:
rule_list.append(rule[0])
# rule_list = [rule[0] for rule in [kwargs.values()] if rule is not None]
return (rule_list,)
@@ -193,11 +126,6 @@ class ToolList:
return {
"optional": {
"tool_1": ("TOOL_LIST",),
"tool_2": ("TOOL_LIST",),
"tool_3": ("TOOL_LIST",),
"tool_4": ("TOOL_LIST",),
"tool_5": ("TOOL_LIST",),
"tool_6": ("TOOL_LIST",),
}
}
@@ -207,25 +135,11 @@ class ToolList:
CATEGORY = "Griptape/Agent Tools"
def create(
self,
tool_1=None,
tool_2=None,
tool_3=None,
tool_4=None,
tool_5=None,
tool_6=None,
):
tool_list = [
tool[0]
for tool in [
tool_1,
tool_2,
tool_3,
tool_4,
tool_5,
tool_6,
]
if tool is not None
]
def create(self, **kwargs):
tool_list = []
tools = [value for value in kwargs.values()]
if len(tools) > 0:
for tool in tools:
tool_list.append(tool[0])
return (tool_list,)
+2
View File
@@ -64,6 +64,7 @@ class gtUICsvExtractionTask(gtUIBaseTask):
"STRING",
{
"multiline": False,
"dynamicPrompts": True,
"default": "Column 1, Column 2, Column 3",
},
),
@@ -115,6 +116,7 @@ class gtUIJsonExtractionTask(gtUIBaseTask):
"STRING",
{
"multiline": True,
"dynamicPrompts": True,
"default": default_schema,
},
),
+3 -1
View File
@@ -12,7 +12,9 @@ class gtUIInputStringNode:
def INPUT_TYPES(s):
return {
"required": {},
"optional": {"STRING": ("STRING", {"multiline": True})},
"optional": {
"STRING": ("STRING", {"multiline": True, "dynamicPrompts": True})
},
}
RETURN_TYPES = ("STRING",)