rearrange and remove components
This commit is contained in:
parent
9b88eab828
commit
0ddb11664f
32 changed files with 310 additions and 16 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
26
src/backend/base/langflow/components/helpers/MergeData.py
Normal file
26
src/backend/base/langflow/components/helpers/MergeData.py
Normal 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
|
||||
22
src/backend/base/langflow/components/helpers/StoreMessage.py
Normal file
22
src/backend/base/langflow/components/helpers/StoreMessage.py
Normal 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
|
||||
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 {
|
||||
115
src/backend/base/langflow/components/prototypes/SubFlow.py
Normal file
115
src/backend/base/langflow/components/prototypes/SubFlow.py
Normal 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
|
||||
39
src/backend/base/langflow/components/prototypes/__init__.py
Normal file
39
src/backend/base/langflow/components/prototypes/__init__.py
Normal 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",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue