rearrange and remove components
This commit is contained in:
parent
9b88eab828
commit
0ddb11664f
32 changed files with 309 additions and 15 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,185 @@
|
|||
from typing import Any, List, Optional, cast
|
||||
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.prompts.chat import HumanMessagePromptTemplate, SystemMessagePromptTemplate
|
||||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.base.agents.utils import AGENTS, AgentSpec, get_agents_list
|
||||
from langflow.field_typing import LanguageModel, Text, Tool
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.dotdict import dotdict
|
||||
|
||||
|
||||
class AgentComponent(LCAgentComponent):
|
||||
display_name = "Agent"
|
||||
description = "Run any LangChain agent using a simplified interface."
|
||||
field_order = [
|
||||
"agent_name",
|
||||
"llm",
|
||||
"tools",
|
||||
"prompt",
|
||||
"tool_template",
|
||||
"handle_parsing_errors",
|
||||
"memory",
|
||||
"input_value",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"agent_name": {
|
||||
"display_name": "Agent",
|
||||
"info": "The agent to use.",
|
||||
"refresh_button": True,
|
||||
"real_time_refresh": True,
|
||||
"options": get_agents_list(),
|
||||
},
|
||||
"llm": {"display_name": "LLM"},
|
||||
"tools": {"display_name": "Tools"},
|
||||
"user_prompt": {
|
||||
"display_name": "Prompt",
|
||||
"multiline": True,
|
||||
"info": "This prompt must contain 'tools' and 'agent_scratchpad' keys.",
|
||||
},
|
||||
"system_message": {
|
||||
"display_name": "System Message",
|
||||
"info": "System message to be passed to the LLM.",
|
||||
"advanced": True,
|
||||
},
|
||||
"tool_template": {
|
||||
"display_name": "Tool Template",
|
||||
"info": "Template for rendering tools in the prompt. Tools have 'name' and 'description' keys.",
|
||||
"advanced": True,
|
||||
},
|
||||
"handle_parsing_errors": {
|
||||
"display_name": "Handle Parsing Errors",
|
||||
"info": "If True, the agent will handle parsing errors. If False, the agent will raise an error.",
|
||||
"advanced": True,
|
||||
},
|
||||
"message_history": {
|
||||
"display_name": "Message History",
|
||||
"info": "Message history to pass to the agent.",
|
||||
},
|
||||
"input_value": {
|
||||
"display_name": "Input",
|
||||
"info": "Input text to pass to the agent.",
|
||||
},
|
||||
"langchain_hub_api_key": {
|
||||
"display_name": "LangChain Hub API Key",
|
||||
"info": "API key to use for LangChain Hub. If provided, prompts will be fetched from LangChain Hub.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_system_and_user_message_from_prompt(self, prompt: Any):
|
||||
"""
|
||||
Extracts the system message and user prompt from a given prompt object.
|
||||
|
||||
Args:
|
||||
prompt (Any): The prompt object from which to extract the system message and user prompt.
|
||||
|
||||
Returns:
|
||||
Tuple[Optional[str], Optional[str]]: A tuple containing the system message and user prompt.
|
||||
If the prompt object does not have any messages, both values will be None.
|
||||
"""
|
||||
if hasattr(prompt, "messages"):
|
||||
system_message = None
|
||||
user_prompt = None
|
||||
for message in prompt.messages:
|
||||
if isinstance(message, SystemMessagePromptTemplate):
|
||||
s_prompt = message.prompt
|
||||
if isinstance(s_prompt, list):
|
||||
s_template = " ".join([cast(str, s.template) for s in s_prompt if hasattr(s, "template")])
|
||||
elif hasattr(s_prompt, "template"):
|
||||
s_template = s_prompt.template
|
||||
system_message = s_template
|
||||
elif isinstance(message, HumanMessagePromptTemplate):
|
||||
h_prompt = message.prompt
|
||||
if isinstance(h_prompt, list):
|
||||
h_template = " ".join([cast(str, h.template) for h in h_prompt if hasattr(h, "template")])
|
||||
elif hasattr(h_prompt, "template"):
|
||||
h_template = h_prompt.template
|
||||
user_prompt = h_template
|
||||
return system_message, user_prompt
|
||||
return None, None
|
||||
|
||||
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
|
||||
"""
|
||||
Updates the build configuration based on the provided field value and field name.
|
||||
|
||||
Args:
|
||||
build_config (dotdict): The build configuration to be updated.
|
||||
field_value (Any): The value of the field being updated.
|
||||
field_name (Text | None, optional): The name of the field being updated. Defaults to None.
|
||||
|
||||
Returns:
|
||||
dotdict: The updated build configuration.
|
||||
"""
|
||||
if field_name == "agent":
|
||||
build_config["agent"]["options"] = get_agents_list()
|
||||
if field_value in AGENTS:
|
||||
# if langchain_hub_api_key is provided, fetch the prompt from LangChain Hub
|
||||
if build_config["langchain_hub_api_key"]["value"] and AGENTS[field_value].hub_repo:
|
||||
from langchain import hub
|
||||
|
||||
hub_repo: str | None = AGENTS[field_value].hub_repo
|
||||
if hub_repo:
|
||||
hub_api_key: str = build_config["langchain_hub_api_key"]["value"]
|
||||
prompt = hub.pull(hub_repo, api_key=hub_api_key)
|
||||
system_message, user_prompt = self.get_system_and_user_message_from_prompt(prompt)
|
||||
if system_message:
|
||||
build_config["system_message"]["value"] = system_message
|
||||
if user_prompt:
|
||||
build_config["user_prompt"]["value"] = user_prompt
|
||||
|
||||
if AGENTS[field_value].prompt:
|
||||
build_config["user_prompt"]["value"] = AGENTS[field_value].prompt
|
||||
else:
|
||||
build_config["user_prompt"]["value"] = "{input}"
|
||||
fields = AGENTS[field_value].fields
|
||||
for field in ["llm", "tools", "prompt", "tools_renderer"]:
|
||||
if field not in fields:
|
||||
build_config[field]["show"] = False
|
||||
return build_config
|
||||
|
||||
async def build(
|
||||
self,
|
||||
agent_name: str,
|
||||
input_value: str,
|
||||
llm: LanguageModel,
|
||||
tools: List[Tool],
|
||||
system_message: str = "You are a helpful assistant. Help the user answer any questions.",
|
||||
user_prompt: str = "{input}",
|
||||
message_history: Optional[List[Data]] = None,
|
||||
tool_template: str = "{name}: {description}",
|
||||
handle_parsing_errors: bool = True,
|
||||
) -> Text:
|
||||
agent_spec: Optional[AgentSpec] = AGENTS.get(agent_name)
|
||||
if agent_spec is None:
|
||||
raise ValueError(f"{agent_name} not found.")
|
||||
|
||||
def render_tool_description(tools):
|
||||
return "\n".join(
|
||||
[tool_template.format(name=tool.name, description=tool.description, args=tool.args) for tool in tools]
|
||||
)
|
||||
|
||||
messages = [
|
||||
("system", system_message),
|
||||
(
|
||||
"placeholder",
|
||||
"{chat_history}",
|
||||
),
|
||||
("human", user_prompt),
|
||||
("placeholder", "{agent_scratchpad}"),
|
||||
]
|
||||
prompt = ChatPromptTemplate.from_messages(messages)
|
||||
agent_func = agent_spec.func
|
||||
agent = agent_func(llm, tools, prompt, render_tool_description, True)
|
||||
result = await self.run_agent(
|
||||
agent=agent,
|
||||
inputs=input_value,
|
||||
tools=tools,
|
||||
message_history=message_history,
|
||||
handle_parsing_errors=handle_parsing_errors,
|
||||
)
|
||||
self.status = result
|
||||
return result
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import re
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import Input, Output, Text
|
||||
|
||||
|
||||
class CodeBlockExtractor(Component):
|
||||
display_name = "Code Block Extractor"
|
||||
description = "Extracts code block from text."
|
||||
|
||||
inputs = [Input(name="text", field_type=Text, description="Text to extract code blocks from.")]
|
||||
|
||||
outputs = [Output(name="code_block", display_name="Code Block", method="get_code_block")]
|
||||
|
||||
def get_code_block(self) -> Text:
|
||||
text = self.text.strip()
|
||||
# Extract code block
|
||||
# It may start with ``` or ```language
|
||||
# It may end with ```
|
||||
pattern = r"^```(?:\w+)?\s*\n(.*?)(?=^```)```"
|
||||
match = re.search(pattern, text, re.MULTILINE)
|
||||
code_block = ""
|
||||
if match:
|
||||
code_block = match.group(1)
|
||||
return code_block
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from typing import List
|
||||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class DocumentsToDataComponent(CustomComponent):
|
||||
display_name = "Documents ⇢ Data"
|
||||
description = "Convert LangChain Documents into Data."
|
||||
icon = "LangChain"
|
||||
|
||||
field_config = {
|
||||
"documents": {"display_name": "Documents"},
|
||||
}
|
||||
|
||||
def build(self, documents: List[Document]) -> List[Data]:
|
||||
if isinstance(documents, Document):
|
||||
documents = [documents]
|
||||
data = [Data.from_document(document) for document in documents]
|
||||
self.status = data
|
||||
return data
|
||||
15
src/backend/base/langflow/components/deactivated/Embed.py
Normal file
15
src/backend/base/langflow/components/deactivated/Embed.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class EmbedComponent(CustomComponent):
|
||||
display_name = "Embed Texts"
|
||||
|
||||
def build_config(self):
|
||||
return {"texts": {"display_name": "Texts"}, "embbedings": {"display_name": "Embeddings"}}
|
||||
|
||||
def build(self, texts: list[str], embbedings: Embeddings) -> Data:
|
||||
vectors = Data(vector=embbedings.embed_documents(texts))
|
||||
self.status = vectors
|
||||
return vectors
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class ExtractKeyFromDataComponent(CustomComponent):
|
||||
display_name = "Extract Key From Data"
|
||||
description = "Extracts a key from a data."
|
||||
beta: bool = True
|
||||
|
||||
field_config = {
|
||||
"data": {"display_name": "Data"},
|
||||
"keys": {
|
||||
"display_name": "Keys",
|
||||
"info": "The keys to extract from the data.",
|
||||
"input_types": [],
|
||||
},
|
||||
"silent_error": {
|
||||
"display_name": "Silent Errors",
|
||||
"info": "If True, errors will not be raised.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, data: Data, keys: list[str], silent_error: bool = True) -> Data:
|
||||
"""
|
||||
Extracts the keys from a data.
|
||||
|
||||
Args:
|
||||
data (Data): The data from which to extract the keys.
|
||||
keys (list[str]): The keys to extract from the data.
|
||||
silent_error (bool): If True, errors will not be raised.
|
||||
|
||||
Returns:
|
||||
dict: The extracted keys.
|
||||
"""
|
||||
extracted_keys = {}
|
||||
for key in keys:
|
||||
try:
|
||||
extracted_keys[key] = getattr(data, key)
|
||||
except AttributeError:
|
||||
if not silent_error:
|
||||
raise KeyError(f"The key '{key}' does not exist in the data.")
|
||||
return_data = Data(data=extracted_keys)
|
||||
self.status = return_data
|
||||
return return_data
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
from typing import List
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class ListFlowsComponent(CustomComponent):
|
||||
display_name = "List Flows"
|
||||
description = "A component to list all available flows."
|
||||
icon = "ListFlows"
|
||||
beta: bool = True
|
||||
|
||||
def build_config(self):
|
||||
return {}
|
||||
|
||||
def build(
|
||||
self,
|
||||
) -> List[Data]:
|
||||
flows = self.list_flows()
|
||||
self.status = flows
|
||||
return flows
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class MergeDataComponent(CustomComponent):
|
||||
display_name = "Merge Data"
|
||||
description = "Merges data."
|
||||
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
|
||||
38
src/backend/base/langflow/components/deactivated/Message.py
Normal file
38
src/backend/base/langflow/components/deactivated/Message.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class MessageComponent(CustomComponent):
|
||||
display_name = "Message"
|
||||
description = "Creates a Message object given a Session ID."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"sender": {
|
||||
"options": ["Machine", "User"],
|
||||
"display_name": "Sender Type",
|
||||
},
|
||||
"sender_name": {"display_name": "Sender Name"},
|
||||
"text": {"display_name": "Text"},
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
sender: str = "User",
|
||||
sender_name: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
text: str = "",
|
||||
) -> Message:
|
||||
message = Message(
|
||||
text=text, sender=sender, sender_name=sender_name, flow_id=self.graph.flow_id, session_id=session_id
|
||||
)
|
||||
|
||||
self.status = message
|
||||
return message
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
from langflow.custom import Component
|
||||
from langflow.field_typing import Text
|
||||
from langflow.io import BoolInput, DropdownInput, MessageTextInput, Output
|
||||
|
||||
|
||||
class SelectivePassThroughComponent(Component):
|
||||
display_name = "Selective Pass Through"
|
||||
description = "Passes the specified value if a specified condition is met."
|
||||
icon = "filter"
|
||||
|
||||
inputs = [
|
||||
MessageTextInput(
|
||||
name="input_value",
|
||||
display_name="Input Value",
|
||||
info="The primary input value to evaluate.",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="comparison_value",
|
||||
display_name="Comparison Value",
|
||||
info="The value to compare against the input value.",
|
||||
),
|
||||
DropdownInput(
|
||||
name="operator",
|
||||
display_name="Operator",
|
||||
options=["equals", "not equals", "contains", "starts with", "ends with"],
|
||||
info="Condition to evaluate the input value.",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="value_to_pass",
|
||||
display_name="Value to Pass",
|
||||
info="The value to pass if the condition is met.",
|
||||
),
|
||||
BoolInput(
|
||||
name="case_sensitive",
|
||||
display_name="Case Sensitive",
|
||||
info="If true, the comparison will be case sensitive.",
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Passed Output", name="passed_output", method="pass_through"),
|
||||
]
|
||||
|
||||
def evaluate_condition(self, input_value: str, comparison_value: str, operator: str, case_sensitive: bool) -> bool:
|
||||
if not case_sensitive:
|
||||
input_value = input_value.lower()
|
||||
comparison_value = comparison_value.lower()
|
||||
|
||||
if operator == "equals":
|
||||
return input_value == comparison_value
|
||||
elif operator == "not equals":
|
||||
return input_value != comparison_value
|
||||
elif operator == "contains":
|
||||
return comparison_value in input_value
|
||||
elif operator == "starts with":
|
||||
return input_value.startswith(comparison_value)
|
||||
elif operator == "ends with":
|
||||
return input_value.endswith(comparison_value)
|
||||
return False
|
||||
|
||||
def pass_through(self) -> Text:
|
||||
input_value = self.input_value
|
||||
comparison_value = self.comparison_value
|
||||
operator = self.operator
|
||||
value_to_pass = self.value_to_pass
|
||||
case_sensitive = self.case_sensitive
|
||||
|
||||
if self.evaluate_condition(input_value, comparison_value, operator, case_sensitive):
|
||||
self.status = value_to_pass
|
||||
return value_to_pass
|
||||
else:
|
||||
self.status = ""
|
||||
return ""
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import LanguageModel, Text
|
||||
|
||||
|
||||
class ShouldRunNextComponent(CustomComponent):
|
||||
display_name = "Should Run Next"
|
||||
description = "Determines if a vertex is runnable."
|
||||
|
||||
def build(self, llm: LanguageModel, question: str, context: str, retries: int = 3) -> Text:
|
||||
template = "Given the following question and the context below, answer with a yes or no.\n\n{error_message}\n\nQuestion: {question}\n\nContext: {context}\n\nAnswer:"
|
||||
|
||||
prompt = PromptTemplate.from_template(template)
|
||||
chain = prompt | llm
|
||||
error_message = ""
|
||||
for i in range(retries):
|
||||
result = chain.invoke(dict(question=question, context=context, error_message=error_message))
|
||||
if isinstance(result, BaseMessage):
|
||||
content = result.content
|
||||
elif isinstance(result, str):
|
||||
content = result
|
||||
if isinstance(content, str) and content.lower().strip() in ["yes", "no"]:
|
||||
break
|
||||
condition = str(content).lower().strip() == "yes"
|
||||
self.status = f"Should Run Next: {condition}"
|
||||
if condition is False:
|
||||
self.stop()
|
||||
return context
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
115
src/backend/base/langflow/components/deactivated/SubFlow.py
Normal file
115
src/backend/base/langflow/components/deactivated/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
|
||||
33
src/backend/base/langflow/components/deactivated/__init__.py
Normal file
33
src/backend/base/langflow/components/deactivated/__init__.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
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 .MergeData import MergeDataComponent
|
||||
from .Notify import NotifyComponent
|
||||
from .PythonFunction import PythonFunctionComponent
|
||||
from .RunFlow import RunFlowComponent
|
||||
from .RunnableExecutor import RunnableExecComponent
|
||||
from .SelectivePassThrough import SelectivePassThroughComponent
|
||||
from .SplitText import SplitTextComponent
|
||||
from .SQLExecutor import SQLExecutorComponent
|
||||
from .SubFlow import SubFlowComponent
|
||||
|
||||
__all__ = [
|
||||
"AgentComponent",
|
||||
"ConditionalRouterComponent",
|
||||
"ExtractKeyFromDataComponent",
|
||||
"FlowToolComponent",
|
||||
"ListenComponent",
|
||||
"ListFlowsComponent",
|
||||
"MergeDataComponent",
|
||||
"NotifyComponent",
|
||||
"PythonFunctionComponent",
|
||||
"RunFlowComponent",
|
||||
"RunnableExecComponent",
|
||||
"SplitTextComponent",
|
||||
"SQLExecutorComponent",
|
||||
"SubFlowComponent",
|
||||
"SelectivePassThroughComponent",
|
||||
]
|
||||
|
|
@ -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
|
||||
70
src/backend/base/langflow/components/helpers/SplitText.py
Normal file
70
src/backend/base/langflow/components/helpers/SplitText.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
from typing import List
|
||||
|
||||
from langchain_text_splitters import CharacterTextSplitter
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.io import HandleInput, IntInput, MessageTextInput, Output
|
||||
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,
|
||||
),
|
||||
MessageTextInput(
|
||||
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
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue