From 7cc577606e7ead557bf1bb28da1e51b37d8bb2be Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sat, 1 Apr 2023 18:38:55 -0300 Subject: [PATCH] feat: first init_agent node --- src/backend/langflow/custom/customs.py | 6 +- .../langflow/interface/agents/custom.py | 23 ++++++- .../langflow/interface/agents/prebuilt.py | 48 ++++++++++++++ .../langflow/interface/prompts/custom.py | 63 +++++++++++++++++++ src/backend/langflow/template/constants.py | 11 ++++ src/backend/langflow/template/nodes.py | 36 +++++++++++ 6 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 src/backend/langflow/interface/agents/prebuilt.py create mode 100644 src/backend/langflow/interface/prompts/custom.py create mode 100644 src/backend/langflow/template/constants.py diff --git a/src/backend/langflow/custom/customs.py b/src/backend/langflow/custom/customs.py index 6a70732a0..ad1d53ee8 100644 --- a/src/backend/langflow/custom/customs.py +++ b/src/backend/langflow/custom/customs.py @@ -3,7 +3,11 @@ from langflow.template import nodes CUSTOM_NODES = { "prompts": {**nodes.ZeroShotPromptNode().to_dict()}, "tools": {**nodes.PythonFunctionNode().to_dict(), **nodes.ToolNode().to_dict()}, - "agents": {**nodes.JsonAgentNode().to_dict(), **nodes.CSVAgentNode().to_dict()}, + "agents": { + **nodes.JsonAgentNode().to_dict(), + **nodes.CSVAgentNode().to_dict(), + **nodes.InitializeAgentNode().to_dict(), + }, } diff --git a/src/backend/langflow/interface/agents/custom.py b/src/backend/langflow/interface/agents/custom.py index 653e3b0be..c74b9a450 100644 --- a/src/backend/langflow/interface/agents/custom.py +++ b/src/backend/langflow/interface/agents/custom.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any, List, Optional from langchain import LLMChain from langchain.agents import AgentExecutor, ZeroShotAgent @@ -8,7 +8,9 @@ from langchain.agents.agent_toolkits.pandas.prompt import PREFIX as PANDAS_PREFI from langchain.agents.agent_toolkits.pandas.prompt import SUFFIX as PANDAS_SUFFIX from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS from langchain.schema import BaseLanguageModel +from langchain.llms.base import BaseLLM from langchain.tools.python.tool import PythonAstREPLTool +from langchain.agents import initialize_agent, Tool class JsonAgent(AgentExecutor): @@ -87,7 +89,26 @@ class CSVAgent(AgentExecutor): return super().run(*args, **kwargs) +class InitializeAgent(AgentExecutor): + """Initialize agent""" + + @classmethod + def initialize(cls, llm: BaseLLM, tools: List[Tool], agent: str): + return initialize_agent( + tools=tools, + llm=llm, + agent=agent, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def run(self, *args, **kwargs): + return super().run(*args, **kwargs) + + CUSTOM_AGENTS = { "JsonAgent": JsonAgent, "CSVAgent": CSVAgent, + "InitializeAgent": InitializeAgent, } diff --git a/src/backend/langflow/interface/agents/prebuilt.py b/src/backend/langflow/interface/agents/prebuilt.py new file mode 100644 index 000000000..ee4dca3a5 --- /dev/null +++ b/src/backend/langflow/interface/agents/prebuilt.py @@ -0,0 +1,48 @@ +from typing import Optional + +from langchain import LLMChain +from langchain.agents import AgentExecutor, ZeroShotAgent +from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX +from langchain.agents.agent_toolkits.json.toolkit import JsonToolkit +from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS +from langchain.schema import BaseLanguageModel +from pydantic import BaseModel + + +class MalfoyAgent(AgentExecutor): + """Json agent""" + + prefix = "Malfoy: " + + @classmethod + def initialize(cls, *args, **kwargs): + return cls.from_toolkit_and_llm(*args, **kwargs) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @classmethod + def from_toolkit_and_llm(cls, toolkit: JsonToolkit, llm: BaseLanguageModel): + tools = toolkit.get_tools() + tool_names = [tool.name for tool in tools] + prompt = ZeroShotAgent.create_prompt( + tools, + prefix=JSON_PREFIX, + suffix=JSON_SUFFIX, + format_instructions=FORMAT_INSTRUCTIONS, + input_variables=None, + ) + llm_chain = LLMChain( + llm=llm, + prompt=prompt, + ) + agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names) + return cls.from_agent_and_tools(agent=agent, tools=tools, verbose=True) + + def run(self, *args, **kwargs): + return super().run(*args, **kwargs) + + +PREBUILT_AGENTS = { + "MalfoyAgent": MalfoyAgent, +} diff --git a/src/backend/langflow/interface/prompts/custom.py b/src/backend/langflow/interface/prompts/custom.py new file mode 100644 index 000000000..e165aa90a --- /dev/null +++ b/src/backend/langflow/interface/prompts/custom.py @@ -0,0 +1,63 @@ +from typing import Any, List, Optional + +from langchain.prompts import PromptTemplate +from langflow.graph.utils import extract_input_variables_from_prompt +from langflow.template.base import Template, TemplateField +from langflow.template.nodes import PromptTemplateNode +from pydantic import root_validator + + +CHARACTER_PROMPT = """I want you to act like {character} from {series}. +I want you to respond and answer like {character}. do not write any explanations. only answer like {character}. +You must know all of the knowledge of {character}.""" + + +class BaseCustomPrompt(PromptTemplate): + template: Optional[str] = None + description: str + human_text: str = "\n {input}" + + @root_validator(pre=False) + def build_template(cls, values): + format_dict = {} + for key in values.get("input_variables", []): + new_value = values[key] + format_dict[key] = new_value + + values["template"] = values["template"].format(**format_dict) + + values["template"] = values["template"] + values["human_text"] + values["input_variables"] = extract_input_variables_from_prompt( + values["template"] + ) + return values + + def build_frontend_node(self) -> PromptTemplateNode: + return PromptTemplateNode( + template=Template( + type_name="test", + fields=[ + TemplateField(name=field, field_type="str", required=True) + for field in self.input_variables + ], + ), + description=self.description, + ) + + +class SeriesCharacterPrompt(BaseCustomPrompt): + # Add a very descriptive description for the prompt generator + description = "A prompt that asks the AI to act like a character from a series." + character: str + series: str + human_text: str = "\n {input}" + template: Optional[str] = CHARACTER_PROMPT + + input_variables: List[str] = ["character", "series"] + + +if __name__ == "__main__": + prompt = SeriesCharacterPrompt(character="Walter White", series="Breaking Bad") + user_input = "I am the one who knocks" + full_prompt = prompt.format(input=user_input) + print(full_prompt) diff --git a/src/backend/langflow/template/constants.py b/src/backend/langflow/template/constants.py new file mode 100644 index 000000000..bb8920a8d --- /dev/null +++ b/src/backend/langflow/template/constants.py @@ -0,0 +1,11 @@ +FORCE_SHOW_FIELDS = [ + "allowed_tools", + "memory", + "prefix", + "examples", + "temperature", + "model_name", + "headers", + "max_value_length", + "max_tokens", +] diff --git a/src/backend/langflow/template/nodes.py b/src/backend/langflow/template/nodes.py index 718d66387..3504bd910 100644 --- a/src/backend/langflow/template/nodes.py +++ b/src/backend/langflow/template/nodes.py @@ -2,6 +2,7 @@ from langchain.agents.mrkl import prompt from langflow.template.base import FrontendNode, Template, TemplateField from langflow.utils.constants import DEFAULT_PYTHON_FUNCTION +from langchain.agents import loading class ZeroShotPromptNode(FrontendNode): @@ -141,6 +142,41 @@ class JsonAgentNode(FrontendNode): return super().to_dict() +class InitializeAgentNode(FrontendNode): + name: str = "InializeAgent" + template: Template = Template( + type_name="initailize_agent", + fields=[ + TemplateField( + field_type="Tool", + required=True, + show=True, + name="tools", + ), + TemplateField( + field_type="BaseLanguageModel", + required=True, + show=True, + name="llm", + ), + TemplateField( + field_type="str", + required=True, + is_list=True, + show=True, + multiline=False, + options=list(loading.AGENT_TO_CLASS.keys()), + name="agent", + ), + ], + ) + description: str = """Construct a json agent from an LLM and tools.""" + base_classes: list[str] = ["AgentExecutor"] + + def to_dict(self): + return super().to_dict() + + class CSVAgentNode(FrontendNode): name: str = "CSVAgent" template: Template = Template(