From 7cc577606e7ead557bf1bb28da1e51b37d8bb2be Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sat, 1 Apr 2023 18:38:55 -0300 Subject: [PATCH 01/12] 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( From bb8c7bfb068c00e5630e1b96a93d5fe6f47dec2d Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sat, 1 Apr 2023 18:39:05 -0300 Subject: [PATCH 02/12] fix: add agent to config --- src/backend/langflow/config.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/backend/langflow/config.yaml b/src/backend/langflow/config.yaml index 5f2d37d74..f45c45fbb 100644 --- a/src/backend/langflow/config.yaml +++ b/src/backend/langflow/config.yaml @@ -8,6 +8,7 @@ agents: - ZeroShotAgent - JsonAgent - CSVAgent + - InitializeAgent prompts: - PromptTemplate @@ -33,13 +34,13 @@ toolkits: - OpenAPIToolkit - JsonToolkit -embeddings: - # +embeddings: [] -vectorstores: - # -documentloaders: - # +vectorstores: [] + + +documentloaders: [] + dev: false From 12c21b018567614a89fe7f323bb252b9f773cdb1 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sat, 1 Apr 2023 18:41:27 -0300 Subject: [PATCH 03/12] custom prompt experiment --- src/backend/langflow/custom/customs.py | 4 ++- src/backend/langflow/template/base.py | 13 ++------ src/backend/langflow/template/nodes.py | 10 +++++++ src/backend/langflow/utils/util.py | 13 ++------ tests/conftest.py | 1 + tests/test_graph.py | 41 ++++++-------------------- tests/test_template.py | 1 + 7 files changed, 28 insertions(+), 55 deletions(-) diff --git a/src/backend/langflow/custom/customs.py b/src/backend/langflow/custom/customs.py index ad1d53ee8..b820cd53c 100644 --- a/src/backend/langflow/custom/customs.py +++ b/src/backend/langflow/custom/customs.py @@ -1,7 +1,9 @@ from langflow.template import nodes CUSTOM_NODES = { - "prompts": {**nodes.ZeroShotPromptNode().to_dict()}, + "prompts": { + **nodes.ZeroShotPromptNode().to_dict(), + }, "tools": {**nodes.PythonFunctionNode().to_dict(), **nodes.ToolNode().to_dict()}, "agents": { **nodes.JsonAgentNode().to_dict(), diff --git a/src/backend/langflow/template/base.py b/src/backend/langflow/template/base.py index eb7b8898b..8ed6cd7e3 100644 --- a/src/backend/langflow/template/base.py +++ b/src/backend/langflow/template/base.py @@ -3,6 +3,7 @@ from typing import Any, Dict, Optional, Union from pydantic import BaseModel +from langflow.template.constants import FORCE_SHOW_FIELDS from langflow.utils import constants @@ -68,17 +69,7 @@ class TemplateFieldCreator(BaseModel, ABC): # Show or not field self.show = bool( (self.required and key not in ["input_variables"]) - or key - in [ - "allowed_tools", - "memory", - "prefix", - "examples", - "temperature", - "model_name", - "headers", - "max_value_length", - ] + or key in FORCE_SHOW_FIELDS or "api_key" in key ) diff --git a/src/backend/langflow/template/nodes.py b/src/backend/langflow/template/nodes.py index 3504bd910..46d39db9d 100644 --- a/src/backend/langflow/template/nodes.py +++ b/src/backend/langflow/template/nodes.py @@ -49,6 +49,16 @@ class ZeroShotPromptNode(FrontendNode): return super().to_dict() +class PromptTemplateNode(FrontendNode): + name: str = "PromptTemplate" + template: Template + description: str + base_classes: list[str] = ["BasePromptTemplate"] + + def to_dict(self): + return super().to_dict() + + class PythonFunctionNode(FrontendNode): name: str = "PythonFunction" template: Template = Template( diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index 59a19eb33..68dfb0a69 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -3,6 +3,7 @@ import inspect import re from typing import Dict, Optional +from langflow.template.constants import FORCE_SHOW_FIELDS from langflow.utils import constants @@ -284,17 +285,7 @@ def format_dict(d, name: Optional[str] = None): # Show or not field value["show"] = bool( (value["required"] and key not in ["input_variables"]) - or key - in [ - "allowed_tools", - "memory", - "prefix", - "examples", - "temperature", - "model_name", - "headers", - "max_value_length", - ] + or key in FORCE_SHOW_FIELDS or "api_key" in key ) diff --git a/tests/conftest.py b/tests/conftest.py index e6eb3562f..7e8316384 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ from pathlib import Path import pytest from fastapi.testclient import TestClient +from langflow.graph.graph import Graph def pytest_configure(): diff --git a/tests/test_graph.py b/tests/test_graph.py index 0ea2d0f51..bbdacc7cb 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -340,43 +340,21 @@ def test_build_params(basic_graph): assert isinstance(llm_node.params["model_name"], str) -def test_build(basic_graph, complex_graph): +def test_build(basic_graph, complex_graph, openapi_graph): """Test Node's build method""" - # def build(self): - # # The params dict is used to build the module - # # it contains values and keys that point to nodes which - # # have their own params dict - # # When build is called, we iterate through the params dict - # # and if the value is a node, we call build on that node - # # and use the output of that build as the value for the param - # # if the value is not a node, then we use the value as the param - # # and continue - # # Another aspect is that the node_type is the class that we need to import - # # and instantiate with these built params + assert_agent_was_built(basic_graph) + assert_agent_was_built(complex_graph) + assert_agent_was_built(openapi_graph) - # # Build each node in the params dict - # for key, value in self.params.items(): - # if isinstance(value, Node): - # self.params[key] = value.build() - # # Get the class from LANGCHAIN_TYPES_DICT - # # and instantiate it with the params - # # and return the instance - # return LANGCHAIN_TYPES_DICT[self.node_type](**self.params) - - assert isinstance(basic_graph, Graph) +def assert_agent_was_built(graph): + """Assert that the agent was built""" + assert isinstance(graph, Graph) # Now we test the build method # Build the Agent - agent = basic_graph.build() + result = graph.build() # The agent should be a AgentExecutor - assert isinstance(agent, AgentExecutor) - - # Now we test the complex example - assert isinstance(complex_graph, Graph) - # Now we test the build method - agent = complex_graph.build() - # The agent should be a AgentExecutor - assert isinstance(agent, AgentExecutor) + assert isinstance(result, AgentExecutor) def test_agent_node_build(basic_graph): @@ -384,7 +362,6 @@ def test_agent_node_build(basic_graph): assert agent_node is not None built_object = agent_node.build() assert built_object is not None - # Add any further assertions specific to the AgentNode's build() method def test_tool_node_build(basic_graph): diff --git a/tests/test_template.py b/tests/test_template.py index 9f7d78c55..f557256e0 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -1,4 +1,5 @@ import importlib +import re from typing import Dict, List, Optional import pytest From 5a231ae941a9e6c1e16bbfb2fed4bdab13770bc8 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sat, 1 Apr 2023 18:57:03 -0300 Subject: [PATCH 04/12] fix: correct text --- src/frontend/src/components/dropdownComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx index d1a0f47b8..dd44080ba 100644 --- a/src/frontend/src/components/dropdownComponent/index.tsx +++ b/src/frontend/src/components/dropdownComponent/index.tsx @@ -5,7 +5,7 @@ import { DropDownComponentType } from "../../types/components"; import { classNames } from "../../utils"; export default function Dropdown({value, options, onSelect}:DropDownComponentType) { - let [internalValue,setInternalValue] = useState(value??"choose an option") + let [internalValue,setInternalValue] = useState(value??"Choose an option") return ( <> { From 43c4fe7dfcb3e15d0c75758564a4d303f309cd72 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sat, 1 Apr 2023 18:58:03 -0300 Subject: [PATCH 05/12] feat: get_signature can return the FrontendNode directly now --- src/backend/langflow/custom/customs.py | 12 +++++------- src/backend/langflow/interface/base.py | 6 ++++-- src/backend/langflow/template/base.py | 2 -- src/backend/langflow/template/nodes.py | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/backend/langflow/custom/customs.py b/src/backend/langflow/custom/customs.py index b820cd53c..0467bac41 100644 --- a/src/backend/langflow/custom/customs.py +++ b/src/backend/langflow/custom/customs.py @@ -1,14 +1,12 @@ from langflow.template import nodes CUSTOM_NODES = { - "prompts": { - **nodes.ZeroShotPromptNode().to_dict(), - }, - "tools": {**nodes.PythonFunctionNode().to_dict(), **nodes.ToolNode().to_dict()}, + "prompts": {"ZeroShotPrompt": nodes.ZeroShotPromptNode()}, + "tools": {"PythonFunction": nodes.PythonFunctionNode(), "Tool": nodes.ToolNode()}, "agents": { - **nodes.JsonAgentNode().to_dict(), - **nodes.CSVAgentNode().to_dict(), - **nodes.InitializeAgentNode().to_dict(), + "JsonAgent": nodes.JsonAgentNode(), + "CSVAgent": nodes.CSVAgentNode(), + "InitializeAgent": nodes.InitializeAgentNode(), }, } diff --git a/src/backend/langflow/interface/base.py b/src/backend/langflow/interface/base.py index eb3432d12..ad8ccfc6a 100644 --- a/src/backend/langflow/interface/base.py +++ b/src/backend/langflow/interface/base.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel @@ -20,7 +20,7 @@ class LangChainTypeCreator(BaseModel, ABC): return self.type_dict @abstractmethod - def get_signature(self, name: str) -> Optional[Dict[Any, Any]]: + def get_signature(self, name: str) -> Union[Optional[Dict[Any, Any]], FrontendNode]: pass @abstractmethod @@ -42,6 +42,8 @@ class LangChainTypeCreator(BaseModel, ABC): signature = self.get_signature(name) if signature is None: raise ValueError(f"{name} not found") + if isinstance(signature, FrontendNode): + return signature fields = [ TemplateField( name=key, diff --git a/src/backend/langflow/template/base.py b/src/backend/langflow/template/base.py index 8ed6cd7e3..87b5b6a8f 100644 --- a/src/backend/langflow/template/base.py +++ b/src/backend/langflow/template/base.py @@ -54,8 +54,6 @@ class TemplateFieldCreator(BaseModel, ABC): if "List" in _type: _type = _type.replace("List[", "")[:-1] self.is_list = True - else: - self.is_list = False # Replace 'Mapping' with 'dict' if "Mapping" in _type: diff --git a/src/backend/langflow/template/nodes.py b/src/backend/langflow/template/nodes.py index 46d39db9d..6b855e5c4 100644 --- a/src/backend/langflow/template/nodes.py +++ b/src/backend/langflow/template/nodes.py @@ -153,7 +153,7 @@ class JsonAgentNode(FrontendNode): class InitializeAgentNode(FrontendNode): - name: str = "InializeAgent" + name: str = "InitializeAgent" template: Template = Template( type_name="initailize_agent", fields=[ From 24bdfaa941f526f1a29c3940aaa3ee8c59813f63 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 09:31:59 -0300 Subject: [PATCH 06/12] refac: formatting moved to FrontendNode --- src/backend/langflow/template/base.py | 103 ++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/src/backend/langflow/template/base.py b/src/backend/langflow/template/base.py index 87b5b6a8f..701bb471f 100644 --- a/src/backend/langflow/template/base.py +++ b/src/backend/langflow/template/base.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Any, Dict, Optional, Union +from typing import Any, Callable, Dict, Optional, Union from pydantic import BaseModel @@ -21,8 +21,6 @@ class TemplateFieldCreator(BaseModel, ABC): content: Union[str, None] = None password: bool = False options: list[str] = [] - # _name will be used to store the name of the field - # in the template name: str = "" def to_dict(self): @@ -60,9 +58,9 @@ class TemplateFieldCreator(BaseModel, ABC): _type = _type.replace("Mapping", "dict") # Change type from str to Tool - self.field_type = "Tool" if key in ["allowed_tools"] else _type + self.field_type = "Tool" if key in {"allowed_tools"} else self.field_type - self.field_type = "int" if key in ["max_value_length"] else self.field_type + self.field_type = "int" if key in {"max_value_length"} else self.field_type # Show or not field self.show = bool( @@ -73,18 +71,18 @@ class TemplateFieldCreator(BaseModel, ABC): # Add password field self.password = any( - text in key.lower() for text in ["password", "token", "api", "key"] + text in key.lower() for text in {"password", "token", "api", "key"} ) # Add multline - self.multiline = key in [ + self.multiline = key in { "suffix", "prefix", "template", "examples", "code", "headers", - ] + } # Replace dict type with str if "dict" in self.field_type.lower(): @@ -120,13 +118,17 @@ class Template(BaseModel): type_name: str fields: list[TemplateField] - def process_fields(self, name: Optional[str] = None) -> None: - for field in self.fields: - signature = field.to_dict() - field.process_field(field.name, signature, name) + def process_fields( + self, + name: Optional[str] = None, + format_field_func: Union[Callable, None] = None, + ): + if format_field_func: + for field in self.fields: + format_field_func(field, name) - def to_dict(self): - self.process_fields(self.type_name) + def to_dict(self, format_field_func=None): + self.process_fields(self.type_name, format_field_func) result = {field.name: field.to_dict() for field in self.fields} result["_type"] = self.type_name # type: ignore return result @@ -141,8 +143,79 @@ class FrontendNode(BaseModel): def to_dict(self): return { self.name: { - "template": self.template.to_dict(), + "template": self.template.to_dict(self.format_field), "description": self.description, "base_classes": self.base_classes, } } + + @staticmethod + def format_field(field: TemplateField, name: Optional[str] = None) -> None: + key = field.name + value = field.to_dict() + _type = value["type"] + + # Remove 'Optional' wrapper + if "Optional" in _type: + _type = _type.replace("Optional[", "")[:-1] + + # Check for list type + if "List" in _type: + _type = _type.replace("List[", "")[:-1] + self.is_list = True + + # Replace 'Mapping' with 'dict' + if "Mapping" in _type: + _type = _type.replace("Mapping", "dict") + + # Change type from str to Tool + field.field_type = "Tool" if key in {"allowed_tools"} else field.field_type + + field.field_type = "int" if key in {"max_value_length"} else field.field_type + + # Show or not field + field.show = bool( + (field.required and key not in ["input_variables"]) + or key in FORCE_SHOW_FIELDS + or "api_key" in key + ) + + # Add password field + field.password = any( + text in key.lower() for text in {"password", "token", "api", "key"} + ) + + # Add multline + field.multiline = key in { + "suffix", + "prefix", + "template", + "examples", + "code", + "headers", + } + + # Replace dict type with str + if "dict" in field.field_type.lower(): + field.field_type = "code" + + if key == "dict_": + field.field_type = "file" + field.suffixes = [".json", ".yaml", ".yml"] + field.file_types = ["json", "yaml", "yml"] + + # Replace default value with actual value + if "default" in value: + field.value = value["default"] + + if key == "headers": + field.value = """{'Authorization': + 'Bearer '}""" + + # Add options to openai + if name == "OpenAI" and key == "model_name": + field.options = constants.OPENAI_MODELS + field.is_list = True + elif name == "OpenAIChat" and key == "model_name": + field.options = constants.CHAT_OPENAI_MODELS + field.is_list = True From f032795fe4ef4688b1cfa520dcd40300a5f0495c Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 09:33:07 -0300 Subject: [PATCH 07/12] feat: working implementation of initialize_agent --- src/backend/langflow/config.yaml | 5 ++- src/backend/langflow/custom/customs.py | 2 +- src/backend/langflow/graph/base.py | 6 +-- src/backend/langflow/interface/agents/base.py | 19 ++++++---- .../langflow/interface/agents/custom.py | 27 +++++++++---- src/backend/langflow/interface/run.py | 9 ++++- src/backend/langflow/template/nodes.py | 38 ++++++++++++------- 7 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/backend/langflow/config.yaml b/src/backend/langflow/config.yaml index f45c45fbb..09fd9ca35 100644 --- a/src/backend/langflow/config.yaml +++ b/src/backend/langflow/config.yaml @@ -8,7 +8,7 @@ agents: - ZeroShotAgent - JsonAgent - CSVAgent - - InitializeAgent + - initialize_agent prompts: - PromptTemplate @@ -34,6 +34,9 @@ toolkits: - OpenAPIToolkit - JsonToolkit +memories: + - ConversationBufferMemory + embeddings: [] diff --git a/src/backend/langflow/custom/customs.py b/src/backend/langflow/custom/customs.py index 0467bac41..112b8db26 100644 --- a/src/backend/langflow/custom/customs.py +++ b/src/backend/langflow/custom/customs.py @@ -6,7 +6,7 @@ CUSTOM_NODES = { "agents": { "JsonAgent": nodes.JsonAgentNode(), "CSVAgent": nodes.CSVAgentNode(), - "InitializeAgent": nodes.InitializeAgentNode(), + "initialize_agent": nodes.InitializeAgentNode(), }, } diff --git a/src/backend/langflow/graph/base.py b/src/backend/langflow/graph/base.py index 08ad786ac..feadf155c 100644 --- a/src/backend/langflow/graph/base.py +++ b/src/backend/langflow/graph/base.py @@ -121,10 +121,10 @@ class Node: f"Required input {key} for module {self.node_type} not found" ) elif value["list"]: - if key in params: + if key not in params: + params[key] = [] + if edge is not None: params[key].append(edge.source) - else: - params[key] = [edge.source] elif value["required"] or edge is not None: params[key] = edge.source elif value["required"] or value.get("value"): diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index 35df3bee6..41ea749e9 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -1,4 +1,5 @@ -from typing import Dict, List +import contextlib +from typing import Dict, Iterable from langchain.agents import loading @@ -31,12 +32,16 @@ class AgentCreator(LangChainTypeCreator): except ValueError as exc: raise ValueError("Agent not found") from exc - def to_list(self) -> List[str]: - return [ - agent.__name__ - for agent in self.type_to_loader_dict.values() - if agent.__name__ in settings.agents or settings.dev - ] + # Now this is a generator + def to_list(self) -> Iterable: + for name, agent in self.type_to_loader_dict.items(): + agent_name = ( + agent.function_name() + if hasattr(agent, "function_name") + else agent.__name__ + ) + if agent_name in settings.agents or settings.dev: + yield agent_name agent_creator = AgentCreator() diff --git a/src/backend/langflow/interface/agents/custom.py b/src/backend/langflow/interface/agents/custom.py index c74b9a450..e5ae77743 100644 --- a/src/backend/langflow/interface/agents/custom.py +++ b/src/backend/langflow/interface/agents/custom.py @@ -11,11 +11,16 @@ 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 +from langchain.memory.chat_memory import BaseChatMemory class JsonAgent(AgentExecutor): """Json agent""" + @staticmethod + def function_name(): + return "JsonAgent" + @classmethod def initialize(cls, *args, **kwargs): return cls.from_toolkit_and_llm(*args, **kwargs) @@ -48,6 +53,10 @@ class JsonAgent(AgentExecutor): class CSVAgent(AgentExecutor): """CSV agent""" + @staticmethod + def function_name(): + return "CSVAgent" + @classmethod def initialize(cls, *args, **kwargs): return cls.from_toolkit_and_llm(*args, **kwargs) @@ -90,15 +99,17 @@ class CSVAgent(AgentExecutor): class InitializeAgent(AgentExecutor): - """Initialize agent""" + """Implementation of initialize_agent function""" + + @staticmethod + def function_name(): + return "initialize_agent" @classmethod - def initialize(cls, llm: BaseLLM, tools: List[Tool], agent: str): - return initialize_agent( - tools=tools, - llm=llm, - agent=agent, - ) + def initialize( + cls, llm: BaseLLM, tools: List[Tool], agent: str, memory: BaseChatMemory + ): + return initialize_agent(tools=tools, llm=llm, agent=agent, memory=memory) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -110,5 +121,5 @@ class InitializeAgent(AgentExecutor): CUSTOM_AGENTS = { "JsonAgent": JsonAgent, "CSVAgent": CSVAgent, - "InitializeAgent": InitializeAgent, + "initialize_agent": InitializeAgent, } diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index cb86789ae..aee7e83ee 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -57,7 +57,14 @@ def get_result_and_thought_using_graph(loaded_langchain, message: str): loaded_langchain.verbose = True try: with io.StringIO() as output_buffer, contextlib.redirect_stdout(output_buffer): - result = loaded_langchain(message) + chat_input = {} + for key in loaded_langchain.input_keys: + if key != "chat_history": + chat_input[key] = message + break + if hasattr(loaded_langchain, "run"): + loaded_langchain = loaded_langchain.run + result = loaded_langchain result = ( result.get(loaded_langchain.output_keys[0]) diff --git a/src/backend/langflow/template/nodes.py b/src/backend/langflow/template/nodes.py index 6b855e5c4..ea72ffd89 100644 --- a/src/backend/langflow/template/nodes.py +++ b/src/backend/langflow/template/nodes.py @@ -153,22 +153,10 @@ class JsonAgentNode(FrontendNode): class InitializeAgentNode(FrontendNode): - name: str = "InitializeAgent" + name: str = "initialize_agent" 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, @@ -178,6 +166,25 @@ class InitializeAgentNode(FrontendNode): options=list(loading.AGENT_TO_CLASS.keys()), name="agent", ), + TemplateField( + field_type="BaseChatMemory", + required=False, + show=True, + name="memory", + ), + TemplateField( + field_type="Tool", + required=False, + show=True, + name="tools", + is_list=True, + ), + TemplateField( + field_type="BaseLanguageModel", + required=True, + show=True, + name="llm", + ), ], ) description: str = """Construct a json agent from an LLM and tools.""" @@ -186,6 +193,11 @@ class InitializeAgentNode(FrontendNode): def to_dict(self): return super().to_dict() + @staticmethod + def format_field(field: TemplateField, name: str): + # do nothing and don't return anything + pass + class CSVAgentNode(FrontendNode): name: str = "CSVAgent" From 03172a19f8a49a48ac108f87db4080eb2c87d95a Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 09:33:31 -0300 Subject: [PATCH 08/12] feat: adding memory --- .../langflow/interface/custom_lists.py | 36 ++++++------------- .../langflow/interface/importing/utils.py | 6 ++++ 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/backend/langflow/interface/custom_lists.py b/src/backend/langflow/interface/custom_lists.py index 59f163338..32d59619b 100644 --- a/src/backend/langflow/interface/custom_lists.py +++ b/src/backend/langflow/interface/custom_lists.py @@ -6,15 +6,8 @@ from langchain.agents import agent_toolkits from langchain.chat_models import ChatOpenAI ## Memory -# from langchain.memory.buffer_window import ConversationBufferWindowMemory -# from langchain.memory.chat_memory import ChatMessageHistory -# from langchain.memory.combined import CombinedMemory -# from langchain.memory.entity import ConversationEntityMemory -# from langchain.memory.kg import ConversationKGMemory -# from langchain.memory.readonly import ReadOnlySharedMemory -# from langchain.memory.simple import SimpleMemory -# from langchain.memory.summary import ConversationSummaryMemory -# from langchain.memory.summary_buffer import ConversationSummaryBufferMemory +from langchain import memory + ## Document Loaders from langchain.document_loaders import ( AirbyteJSONLoader, @@ -104,23 +97,6 @@ llm_type_to_cls_dict = llms.type_to_cls_dict llm_type_to_cls_dict["openai-chat"] = ChatOpenAI # type: ignore -## Memory - -memory_type_to_cls_dict: dict[str, Any] = { - # "CombinedMemory": CombinedMemory, - # "ConversationBufferWindowMemory": ConversationBufferWindowMemory, - # "ConversationBufferMemory": ConversationBufferMemory, - # "SimpleMemory": SimpleMemory, - # "ConversationSummaryBufferMemory": ConversationSummaryBufferMemory, - # "ConversationKGMemory": ConversationKGMemory, - # "ConversationEntityMemory": ConversationEntityMemory, - # "ConversationSummaryMemory": ConversationSummaryMemory, - # "ChatMessageHistory": ChatMessageHistory, - # "ConversationStringBufferMemory": ConversationStringBufferMemory, - # "ReadOnlySharedMemory": ReadOnlySharedMemory, -} - - ## Chain # from langchain.chains.loading import type_to_loader_dict # from langchain.chains.conversation.base import ConversationChain @@ -142,6 +118,14 @@ toolkit_type_to_cls_dict: dict[str, Any] = { if not toolkit_name.islower() } +## Memory + + +memory_type_to_cls_dict: dict[str, Any] = { + memory_name: import_class(f"langchain.memory.{memory_name}") + for memory_name in memory.__all__ +} + wrapper_type_to_cls_dict: dict[str, Any] = { wrapper.__name__: wrapper for wrapper in [requests.RequestsWrapper] diff --git a/src/backend/langflow/interface/importing/utils.py b/src/backend/langflow/interface/importing/utils.py index f054ddc26..b8717f804 100644 --- a/src/backend/langflow/interface/importing/utils.py +++ b/src/backend/langflow/interface/importing/utils.py @@ -36,10 +36,16 @@ def import_by_type(_type: str, name: str) -> Any: "chains": import_chain, "toolkits": import_toolkit, "wrappers": import_wrapper, + "memory": import_memory, } return func_dict[_type](name) +def import_memory(memory: str) -> Any: + """Import memory from memory name""" + return import_module(f"from langchain.memory import {memory}") + + def import_class(class_path: str) -> Any: """Import class from class path""" module_path, class_name = class_path.rsplit(".", 1) From 9891d6b68aa6e0174102e7d00567264ca1c488bd Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 09:51:53 -0300 Subject: [PATCH 09/12] linting --- src/backend/langflow/interface/agents/base.py | 9 +++++---- src/backend/langflow/interface/agents/prebuilt.py | 2 -- src/backend/langflow/interface/prompts/custom.py | 14 ++++++++------ src/backend/langflow/template/base.py | 2 +- src/backend/langflow/template/nodes.py | 3 ++- tests/conftest.py | 1 - tests/test_template.py | 1 - 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index 41ea749e9..d0ab903c1 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -1,5 +1,4 @@ -import contextlib -from typing import Dict, Iterable +from typing import Dict, List from langchain.agents import loading @@ -33,7 +32,8 @@ class AgentCreator(LangChainTypeCreator): raise ValueError("Agent not found") from exc # Now this is a generator - def to_list(self) -> Iterable: + def to_list(self) -> List[str]: + names = [] for name, agent in self.type_to_loader_dict.items(): agent_name = ( agent.function_name() @@ -41,7 +41,8 @@ class AgentCreator(LangChainTypeCreator): else agent.__name__ ) if agent_name in settings.agents or settings.dev: - yield agent_name + names.append(name) + return names agent_creator = AgentCreator() diff --git a/src/backend/langflow/interface/agents/prebuilt.py b/src/backend/langflow/interface/agents/prebuilt.py index ee4dca3a5..4160b9809 100644 --- a/src/backend/langflow/interface/agents/prebuilt.py +++ b/src/backend/langflow/interface/agents/prebuilt.py @@ -1,4 +1,3 @@ -from typing import Optional from langchain import LLMChain from langchain.agents import AgentExecutor, ZeroShotAgent @@ -6,7 +5,6 @@ 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): diff --git a/src/backend/langflow/interface/prompts/custom.py b/src/backend/langflow/interface/prompts/custom.py index e165aa90a..a33e20e1c 100644 --- a/src/backend/langflow/interface/prompts/custom.py +++ b/src/backend/langflow/interface/prompts/custom.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import List, Optional from langchain.prompts import PromptTemplate from langflow.graph.utils import extract_input_variables_from_prompt @@ -13,8 +13,8 @@ You must know all of the knowledge of {character}.""" class BaseCustomPrompt(PromptTemplate): - template: Optional[str] = None - description: str + template: str = "" + description: Optional[str] human_text: str = "\n {input}" @root_validator(pre=False) @@ -41,17 +41,19 @@ class BaseCustomPrompt(PromptTemplate): for field in self.input_variables ], ), - description=self.description, + description=self.description or "", ) 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." + description: Optional[ + str + ] = "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 + template: str = CHARACTER_PROMPT input_variables: List[str] = ["character", "series"] diff --git a/src/backend/langflow/template/base.py b/src/backend/langflow/template/base.py index 701bb471f..63cae2130 100644 --- a/src/backend/langflow/template/base.py +++ b/src/backend/langflow/template/base.py @@ -162,7 +162,7 @@ class FrontendNode(BaseModel): # Check for list type if "List" in _type: _type = _type.replace("List[", "")[:-1] - self.is_list = True + field.is_list = True # Replace 'Mapping' with 'dict' if "Mapping" in _type: diff --git a/src/backend/langflow/template/nodes.py b/src/backend/langflow/template/nodes.py index ea72ffd89..6bd23d59a 100644 --- a/src/backend/langflow/template/nodes.py +++ b/src/backend/langflow/template/nodes.py @@ -1,3 +1,4 @@ +from typing import Optional from langchain.agents.mrkl import prompt from langflow.template.base import FrontendNode, Template, TemplateField @@ -194,7 +195,7 @@ class InitializeAgentNode(FrontendNode): return super().to_dict() @staticmethod - def format_field(field: TemplateField, name: str): + def format_field(field: TemplateField, name: Optional[str] = None) -> None: # do nothing and don't return anything pass diff --git a/tests/conftest.py b/tests/conftest.py index 7e8316384..e6eb3562f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,6 @@ from pathlib import Path import pytest from fastapi.testclient import TestClient -from langflow.graph.graph import Graph def pytest_configure(): diff --git a/tests/test_template.py b/tests/test_template.py index f557256e0..9f7d78c55 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -1,5 +1,4 @@ import importlib -import re from typing import Dict, List, Optional import pytest From f38c02deccae78153436cfc3da16f36d417322ea Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 10:11:08 -0300 Subject: [PATCH 10/12] blacken --- src/backend/langflow/interface/agents/prebuilt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/langflow/interface/agents/prebuilt.py b/src/backend/langflow/interface/agents/prebuilt.py index 4160b9809..e20ec3bde 100644 --- a/src/backend/langflow/interface/agents/prebuilt.py +++ b/src/backend/langflow/interface/agents/prebuilt.py @@ -1,4 +1,3 @@ - from langchain import LLMChain from langchain.agents import AgentExecutor, ZeroShotAgent from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX From 58652f7c2b57760ad878e6347ec1f72174e3a7cf Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 10:41:40 -0300 Subject: [PATCH 11/12] fix: loading agent correctly and added correct chat models loading --- src/backend/langflow/config.yaml | 2 +- src/backend/langflow/graph/base.py | 4 +++- src/backend/langflow/graph/graph.py | 10 ++++++++-- src/backend/langflow/interface/agents/base.py | 2 +- .../langflow/interface/importing/utils.py | 17 ++++++++++++++--- src/backend/langflow/template/base.py | 4 ++-- src/backend/langflow/utils/util.py | 2 +- tests/data/Openapi.json | 6 +++--- tests/test_template.py | 4 ++-- 9 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/backend/langflow/config.yaml b/src/backend/langflow/config.yaml index 09fd9ca35..08beaae08 100644 --- a/src/backend/langflow/config.yaml +++ b/src/backend/langflow/config.yaml @@ -16,7 +16,7 @@ prompts: llms: - OpenAI - - OpenAIChat + - ChatOpenAI tools: - Search diff --git a/src/backend/langflow/graph/base.py b/src/backend/langflow/graph/base.py index feadf155c..24b8a31c6 100644 --- a/src/backend/langflow/graph/base.py +++ b/src/backend/langflow/graph/base.py @@ -179,7 +179,9 @@ class Node: params=self.params, ) except Exception as exc: - raise ValueError(f"Error building node {self.node_type}") from exc + raise ValueError( + f"Error building node {self.node_type}: {str(exc)}" + ) from exc if self._built_object is None: raise ValueError(f"Node type {self.node_type} not found") diff --git a/src/backend/langflow/graph/graph.py b/src/backend/langflow/graph/graph.py index ad6cb8d28..8bd2028ca 100644 --- a/src/backend/langflow/graph/graph.py +++ b/src/backend/langflow/graph/graph.py @@ -106,7 +106,10 @@ class Graph: if node_type in prompt_creator.to_list(): nodes.append(PromptNode(node)) - elif node_type in agent_creator.to_list(): + elif ( + node_type in agent_creator.to_list() + or node_lc_type in agent_creator.to_list() + ): nodes.append(AgentNode(node)) elif node_type in chain_creator.to_list(): nodes.append(ChainNode(node)) @@ -118,7 +121,10 @@ class Graph: nodes.append(ToolkitNode(node)) elif node_type in wrapper_creator.to_list(): nodes.append(WrapperNode(node)) - elif node_type in llm_creator.to_list(): + elif ( + node_type in llm_creator.to_list() + or node_lc_type in llm_creator.to_list() + ): nodes.append(LLMNode(node)) else: nodes.append(Node(node)) diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index d0ab903c1..464c04276 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -41,7 +41,7 @@ class AgentCreator(LangChainTypeCreator): else agent.__name__ ) if agent_name in settings.agents or settings.dev: - names.append(name) + names.append(agent_name) return names diff --git a/src/backend/langflow/interface/importing/utils.py b/src/backend/langflow/interface/importing/utils.py index b8717f804..1500c448a 100644 --- a/src/backend/langflow/interface/importing/utils.py +++ b/src/backend/langflow/interface/importing/utils.py @@ -8,7 +8,7 @@ from langchain.agents import Agent from langchain.chains.base import Chain from langchain.llms.base import BaseLLM from langchain.tools import BaseTool - +from langchain.chat_models.base import BaseChatModel from langflow.interface.tools.util import get_tool_by_name @@ -31,14 +31,25 @@ def import_by_type(_type: str, name: str) -> Any: func_dict = { "agents": import_agent, "prompts": import_prompt, - "llms": import_llm, + "llms": {"llm": import_llm, "chat": import_chat_llm}, "tools": import_tool, "chains": import_chain, "toolkits": import_toolkit, "wrappers": import_wrapper, "memory": import_memory, } - return func_dict[_type](name) + if _type == "llms": + key = "chat" if "chat" in name.lower() else "llm" + loaded_func = func_dict[_type][key] + else: + loaded_func = func_dict[_type] + + return loaded_func(name) + + +def import_chat_llm(llm: str) -> BaseChatModel: + """Import chat llm from llm name""" + return import_class(f"langchain.chat_models.{llm}") def import_memory(memory: str) -> Any: diff --git a/src/backend/langflow/template/base.py b/src/backend/langflow/template/base.py index 63cae2130..887ab187f 100644 --- a/src/backend/langflow/template/base.py +++ b/src/backend/langflow/template/base.py @@ -105,7 +105,7 @@ class TemplateFieldCreator(BaseModel, ABC): if name == "OpenAI" and key == "model_name": self.options = constants.OPENAI_MODELS self.is_list = True - elif name == "OpenAIChat" and key == "model_name": + elif name == "ChatOpenAI" and key == "model_name": self.options = constants.CHAT_OPENAI_MODELS self.is_list = True @@ -216,6 +216,6 @@ class FrontendNode(BaseModel): if name == "OpenAI" and key == "model_name": field.options = constants.OPENAI_MODELS field.is_list = True - elif name == "OpenAIChat" and key == "model_name": + elif name == "ChatOpenAI" and key == "model_name": field.options = constants.CHAT_OPENAI_MODELS field.is_list = True diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index 68dfb0a69..1ca73746b 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -327,7 +327,7 @@ def format_dict(d, name: Optional[str] = None): if name == "OpenAI" and key == "model_name": value["options"] = constants.OPENAI_MODELS value["list"] = True - elif name == "OpenAIChat" and key == "model_name": + elif name == "ChatOpenAI" and key == "model_name": value["options"] = constants.CHAT_OPENAI_MODELS value["list"] = True diff --git a/tests/data/Openapi.json b/tests/data/Openapi.json index d51404bbb..1d880aa9d 100644 --- a/tests/data/Openapi.json +++ b/tests/data/Openapi.json @@ -267,7 +267,7 @@ "y": 514.9920887988924 }, "data": { - "type": "OpenAIChat", + "type": "ChatOpenAI", "node": { "template": { "cache": { @@ -365,7 +365,7 @@ "type": "bool", "list": false }, - "_type": "OpenAIChat" + "_type": "ChatOpenAI" }, "description": "Wrapper around OpenAI Chat large language models.To use, you should have the ``openai`` python package installed, and theenvironment variable ``OPENAI_API_KEY`` set with your API key.Any parameters that are valid to be passed to the openai.create call can be passedin, even if not explicitly saved on this class.", "base_classes": [ @@ -423,7 +423,7 @@ }, { "source": "dndnode_36", - "sourceHandle": "OpenAIChat|dndnode_36|BaseLanguageModel|BaseLLM", + "sourceHandle": "ChatOpenAI|dndnode_36|BaseLanguageModel|BaseLLM", "target": "dndnode_33", "targetHandle": "BaseLanguageModel|llm|dndnode_33", "className": "animate-pulse", diff --git a/tests/test_template.py b/tests/test_template.py index 9f7d78c55..b5a424dab 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -210,7 +210,7 @@ def test_format_dict(): } assert format_dict(input_dict) == expected_output - # Test 7: Check class name-specific cases (OpenAI, OpenAIChat) + # Test 7: Check class name-specific cases (OpenAI, ChatOpenAI) input_dict = { "model_name": {"type": "str", "required": False}, } @@ -237,7 +237,7 @@ def test_format_dict(): }, } assert format_dict(input_dict, "OpenAI") == expected_output_openai - assert format_dict(input_dict, "OpenAIChat") == expected_output_openai_chat + assert format_dict(input_dict, "ChatOpenAI") == expected_output_openai_chat # Test 8: Replace dict type with str input_dict = { From 5a2fca7eb54abf1586d1af87afb69a3113b18ef0 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Sun, 2 Apr 2023 10:42:24 -0300 Subject: [PATCH 12/12] fix: ignore type for specific case --- src/backend/langflow/interface/importing/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/interface/importing/utils.py b/src/backend/langflow/interface/importing/utils.py index 1500c448a..96dadc438 100644 --- a/src/backend/langflow/interface/importing/utils.py +++ b/src/backend/langflow/interface/importing/utils.py @@ -40,7 +40,7 @@ def import_by_type(_type: str, name: str) -> Any: } if _type == "llms": key = "chat" if "chat" in name.lower() else "llm" - loaded_func = func_dict[_type][key] + loaded_func = func_dict[_type][key] # type: ignore else: loaded_func = func_dict[_type]