rearrange and remove components

This commit is contained in:
Rodrigo 2024-06-23 19:40:46 -03:00 committed by Gabriel Luiz Freitas Almeida
commit 0ddb11664f
32 changed files with 310 additions and 16 deletions

View file

@ -1,19 +1,29 @@
from typing import List, cast
from typing import List, Optional
from langchain.agents import AgentExecutor, BaseSingleActionAgent
from langchain.agents.tool_calling_agent.base import create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
<<<<<<< Updated upstream
from langflow.custom import Component
from langflow.io import BoolInput, HandleInput, MessageTextInput, Output
from langflow.schema import Data
=======
from langchain.agents import AgentExecutor
from langchain_core.messages import BaseMessage
>>>>>>> Stashed changes
from langflow.schema.message import Message
from langflow.custom import Component
from langflow.io import HandleInput, TextInput, BoolInput, Output
from langflow.schema import Data
class ToolCallingAgentComponent(Component):
display_name: str = "Tool Calling Agent"
description: str = "Agent that uses tools. Only models that are compatible with function calling are supported."
description: str = (
"Agent that uses tools. Only models that are compatible with function calling are supported."
)
icon = "LangChain"
beta = True
inputs = [
MessageTextInput(
@ -80,12 +90,12 @@ class ToolCallingAgentComponent(Component):
agent = create_tool_calling_agent(self.llm, self.tools, prompt)
runnable = AgentExecutor.from_agent_and_tools(
agent=cast(BaseSingleActionAgent, agent),
agent=agent,
tools=self.tools,
verbose=True,
handle_parsing_errors=self.handle_parsing_errors,
)
input_dict: dict[str, str | list[dict[str, str]]] = {"input": self.input_value}
input_dict: dict[str, str | list[BaseMessage]] = {"input": self.input_value}
if hasattr(self, "memory") and self.memory:
input_dict["chat_history"] = self.convert_chat_history(self.memory)
result = await runnable.ainvoke(input_dict)
@ -98,7 +108,7 @@ class ToolCallingAgentComponent(Component):
return Message(text=result_string)
def convert_chat_history(self, chat_history: List[Data]) -> List[dict[str, str]]:
def convert_chat_history(self, chat_history: List[Data]) -> List[Dict[str, str]]:
messages = []
for item in chat_history:
role = "user" if item.sender == "User" else "assistant"

View file

@ -0,0 +1,69 @@
from typing import List
from langchain_text_splitters import CharacterTextSplitter
from langflow.custom import Component
from langflow.io import HandleInput, IntInput, Output, TextInput
from langflow.schema import Data
from langflow.utils.util import unescape_string
class SplitTextComponent(Component):
display_name: str = "Split Text"
description: str = "Split text into chunks based on specified criteria."
icon = "scissors-line-dashed"
inputs = [
HandleInput(
name="data_inputs",
display_name="Data Inputs",
info="The data to split.",
input_types=["Data"],
is_list=True,
),
IntInput(
name="chunk_overlap",
display_name="Chunk Overlap",
info="Number of characters to overlap between chunks.",
value=200,
),
IntInput(
name="chunk_size",
display_name="Chunk Size",
info="The maximum number of characters in each chunk.",
value=1000,
),
TextInput(
name="separator",
display_name="Separator",
info="The character to split on. Defaults to newline.",
value="\n",
),
]
outputs = [
Output(display_name="Chunks", name="chunks", method="split_text"),
]
def _docs_to_data(self, docs):
data = []
for doc in docs:
data.append(Data(text=doc.page_content, data=doc.metadata))
return data
def split_text(self) -> List[Data]:
separator = unescape_string(self.separator)
documents = []
for _input in self.data_inputs:
if isinstance(_input, Data):
documents.append(_input.to_lc_document())
splitter = CharacterTextSplitter(
chunk_overlap=self.chunk_overlap,
chunk_size=self.chunk_size,
separator=separator,
)
docs = splitter.split_documents(documents)
data = self._docs_to_data(docs)
self.status = data
return data

View file

@ -9,6 +9,7 @@ class FilterDataComponent(Component):
display_name = "Filter Data"
description = "Filters a Data object based on a list of keys."
icon = "filter"
beta = True
inputs = [
DataInput(

View file

@ -5,14 +5,12 @@ from langflow.custom import CustomComponent
class UUIDGeneratorComponent(CustomComponent):
documentation: str = "http://docs.langflow.org/components/custom"
display_name = "ID Generator"
description = "Generates a unique ID."
def update_build_config(
self,
build_config: dict,
field_value: Any,
field_name: Optional[str] = None,
):
if field_name == "unique_id":

View file

@ -0,0 +1,26 @@
from langflow.custom import CustomComponent
from langflow.schema import Data
class MergeDataComponent(CustomComponent):
display_name = "Merge Data"
description = "Combines multiple data sources into a single unified Data object."
beta: bool = True
field_config = {
"data": {"display_name": "Data"},
}
def build(self, data: list[Data]) -> Data:
if not data:
return Data()
if len(data) == 1:
return data[0]
merged_data = Data()
for value in data:
if merged_data is None:
merged_data = value
else:
merged_data += value
self.status = merged_data
return merged_data

View file

@ -0,0 +1,22 @@
from langflow.custom import CustomComponent
from langflow.memory import get_messages, store_message
from langflow.schema.message import Message
class StoreMessageComponent(CustomComponent):
display_name = "Store Message"
description = "Stores a chat message."
def build_config(self):
return {
"message": {"display_name": "Message"},
}
def build(
self,
message: Message,
) -> Message:
store_message(message, flow_id=self.graph.flow_id)
self.status = get_messages()
return message

View file

@ -1,15 +1,26 @@
from .CombineText import CombineTextComponent
from .CreateData import CreateDataComponent
from .CustomComponent import Component
from .ParseData import ParseDataComponent
from .DocumentsToData import DocumentsToDataComponent
from .CustomComponent import CustomComponent
from .FilterData import FilterDataComponent
from .IDGenerator import UUIDGeneratorComponent
from .Memory import MemoryComponent
from .MergeData import MergeDataComponent
from .ParseData import ParseDataComponent
from .SplitText import SplitTextComponent
from .StoreMessage import StoreMessageComponent
from .UpdateData import UpdateDataComponent
__all__ = [
"Component",
"UpdateDataComponent",
"DocumentsToDataComponent",
"UUIDGeneratorComponent",
"ParseDataComponent",
"CombineTextComponent",
"CreateDataComponent",
"CustomComponent",
"FilterDataComponent",
"UUIDGeneratorComponent",
"MemoryComponent",
"MergeDataComponent",
"ParseDataComponent",
"SplitTextComponent",
"StoreMessageComponent",
"UpdateDataComponent",
]

View file

@ -16,6 +16,7 @@ class FlowToolComponent(CustomComponent):
description = "Construct a Tool from a function that runs the loaded Flow."
field_order = ["flow_name", "name", "description", "return_direct"]
trace_type = "tool"
beta = True
def get_flow_names(self) -> List[str]:
flow_datas = self.list_flows()

View file

@ -9,6 +9,7 @@ class PassComponent(CustomComponent):
display_name = "Pass"
description = "A pass-through component that forwards the second input while ignoring the first, used for controlling workflow direction."
field_order = ["ignored_input", "forwarded_input"]
beta = True
def build_config(self) -> dict:
return {

View file

@ -9,6 +9,7 @@ class PythonFunctionComponent(CustomComponent):
display_name = "Python Function"
description = "Define a Python function."
icon = "Python"
beta = True
def build_config(self):
return {

View file

@ -0,0 +1,115 @@
from typing import Any, List, Optional
from langflow.base.flow_processing.utils import build_data_from_result_data
from langflow.custom import CustomComponent
from langflow.graph.graph.base import Graph
from langflow.graph.schema import RunOutputs
from langflow.graph.vertex.base import Vertex
from langflow.helpers.flow import get_flow_inputs
from langflow.schema import Data
from langflow.schema.dotdict import dotdict
from langflow.template.field.base import Input
from loguru import logger
class SubFlowComponent(CustomComponent):
display_name = "Sub Flow"
description = (
"Dynamically Generates a Component from a Flow. The output is a list of data with keys 'result' and 'message'."
)
beta: bool = True
field_order = ["flow_name"]
def get_flow_names(self) -> List[str]:
flow_datas = self.list_flows()
return [flow_data.data["name"] for flow_data in flow_datas]
def get_flow(self, flow_name: str) -> Optional[Data]:
flow_datas = self.list_flows()
for flow_data in flow_datas:
if flow_data.data["name"] == flow_name:
return flow_data
return None
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
logger.debug(f"Updating build config with field value {field_value} and field name {field_name}")
if field_name == "flow_name":
build_config["flow_name"]["options"] = self.get_flow_names()
# Clean up the build config
for key in list(build_config.keys()):
if key not in self.field_order + ["code", "_type", "get_final_results_only"]:
del build_config[key]
if field_value is not None and field_name == "flow_name":
try:
flow_data = self.get_flow(field_value)
if not flow_data:
raise ValueError(f"Flow {field_value} not found.")
graph = Graph.from_payload(flow_data.data["data"])
# Get all inputs from the graph
inputs = get_flow_inputs(graph)
# Add inputs to the build config
build_config = self.add_inputs_to_build_config(inputs, build_config)
except Exception as e:
logger.error(f"Error getting flow {field_value}: {str(e)}")
return build_config
def add_inputs_to_build_config(self, inputs: List[Vertex], build_config: dotdict):
new_fields: list[Input] = []
for vertex in inputs:
field = Input(
display_name=vertex.display_name,
name=vertex.id,
info=vertex.description,
field_type="str",
value=None,
)
new_fields.append(field)
logger.debug(new_fields)
for field in new_fields:
build_config[field.name] = field.to_dict()
return build_config
def build_config(self):
return {
"input_value": {
"display_name": "Input Value",
"multiline": True,
},
"flow_name": {
"display_name": "Flow Name",
"info": "The name of the flow to run.",
"options": [],
"real_time_refresh": True,
"refresh_button": True,
},
"tweaks": {
"display_name": "Tweaks",
"info": "Tweaks to apply to the flow.",
},
"get_final_results_only": {
"display_name": "Get Final Results Only",
"info": "If False, the output will contain all outputs from the flow.",
"advanced": True,
},
}
async def build(self, flow_name: str, get_final_results_only: bool = True, **kwargs) -> List[Data]:
tweaks = {key: {"input_value": value} for key, value in kwargs.items()}
run_outputs: List[Optional[RunOutputs]] = await self.run_flow(
tweaks=tweaks,
flow_name=flow_name,
)
if not run_outputs:
return []
run_output = run_outputs[0]
data = []
if run_output is not None:
for output in run_output.outputs:
if output:
data.extend(build_data_from_result_data(output, get_final_results_only))
self.status = data
logger.debug(data)
return data

View file

@ -0,0 +1,39 @@
# from .AgentComponent import AgentComponent
# from .ConditionalRouter import ConditionalRouterComponent
# from .ExtractKeyFromData import ExtractKeyFromDataComponent
# from .FlowTool import FlowToolComponent
# from .Listen import ListenComponent
# from .ListFlows import ListFlowsComponent
# from ..helpers.MergeData import MergeDataComponent
# from .Notify import NotifyComponent
# from .PythonFunction import PythonFunctionComponent
# from .RunFlow import RunFlowComponent
# from .RunnableExecutor import RunnableExecComponent
# from .SelectivePassThrough import SelectivePassThroughComponent
# from ..helpers.SplitText import SplitTextComponent
# from .SQLExecutor import SQLExecutorComponent
# from .SubFlow import SubFlowComponent
from .ConditionalRouter import ConditionalRouterComponent
from .FlowTool import FlowToolComponent
from .Listen import ListenComponent
from .Notify import NotifyComponent
from .Pass import PassComponent
from .PythonFunction import PythonFunctionComponent
from .RunFlow import RunFlowComponent
from .RunnableExecutor import RunnableExecComponent
from .SQLExecutor import SQLExecutorComponent
from .SubFlow import SubFlowComponent
__all__ = [
"ConditionalRouterComponent",
"FlowToolComponent",
"ListenComponent",
"NotifyComponent",
"PassComponent",
"PythonFunctionComponent",
"RunFlowComponent",
"RunnableExecComponent",
"SQLExecutorComponent",
"SubFlowComponent",
]