rename utilities folder to helpers
This commit is contained in:
parent
7c8c52dbc3
commit
d770a3747b
17 changed files with 1 additions and 7 deletions
109
src/backend/langflow/components/helpers/APIRequest.py
Normal file
109
src/backend/langflow/components/helpers/APIRequest.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import asyncio
|
||||
from typing import List, Optional, Union
|
||||
import httpx
|
||||
|
||||
import requests
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
|
||||
class APIRequest(CustomComponent):
|
||||
display_name: str = "API Request"
|
||||
description: str = "Make an HTTP request to the given URL."
|
||||
output_types: list[str] = ["Record"]
|
||||
documentation: str = "https://docs.langflow.org/components/utilities#api-request"
|
||||
beta: bool = True
|
||||
field_config = {
|
||||
"url": {"display_name": "URL", "info": "The URL to make the request to."},
|
||||
"method": {
|
||||
"display_name": "Method",
|
||||
"info": "The HTTP method to use.",
|
||||
"field_type": "str",
|
||||
"options": ["GET", "POST", "PATCH", "PUT"],
|
||||
"value": "GET",
|
||||
},
|
||||
"headers": {
|
||||
"display_name": "Headers",
|
||||
"info": "The headers to send with the request.",
|
||||
},
|
||||
"record": {
|
||||
"display_name": "Record",
|
||||
"info": "The record to send with the request (for POST, PATCH, PUT).",
|
||||
},
|
||||
"timeout": {
|
||||
"display_name": "Timeout",
|
||||
"field_type": "int",
|
||||
"info": "The timeout to use for the request.",
|
||||
"value": 5,
|
||||
},
|
||||
}
|
||||
|
||||
async def make_request(
|
||||
self,
|
||||
session: requests.Session,
|
||||
method: str,
|
||||
url: str,
|
||||
headers: Optional[dict] = None,
|
||||
record: Optional[Record] = None,
|
||||
timeout: int = 5,
|
||||
) -> Record:
|
||||
method = method.upper()
|
||||
if method not in ["GET", "POST", "PATCH", "PUT"]:
|
||||
raise ValueError(f"Unsupported method: {method}")
|
||||
|
||||
data = record.text if record else None
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.request(
|
||||
method, url, headers=headers, content=data, timeout=timeout
|
||||
)
|
||||
try:
|
||||
response_json = response.json()
|
||||
result = orjson_dumps(response_json, indent_2=False)
|
||||
except Exception:
|
||||
result = response.text
|
||||
return Record(
|
||||
text=result,
|
||||
data={
|
||||
"source": url,
|
||||
"headers": headers,
|
||||
"status_code": response.status_code,
|
||||
},
|
||||
)
|
||||
except httpx.TimeoutException:
|
||||
return Record(
|
||||
text="Request Timed Out",
|
||||
data={"source": url, "headers": headers, "status_code": 408},
|
||||
)
|
||||
except Exception as exc:
|
||||
return Record(
|
||||
text=str(exc),
|
||||
data={"source": url, "headers": headers, "status_code": 500},
|
||||
)
|
||||
|
||||
async def build(
|
||||
self,
|
||||
method: str,
|
||||
url: List[str],
|
||||
headers: Optional[dict] = None,
|
||||
record: Optional[Union[Record, List[Record]]] = None,
|
||||
timeout: int = 5,
|
||||
) -> List[Record]:
|
||||
if headers is None:
|
||||
headers = {}
|
||||
urls = url if isinstance(url, list) else [url]
|
||||
records = (
|
||||
record
|
||||
if isinstance(record, list)
|
||||
else [record] if record else [None] * len(urls)
|
||||
)
|
||||
|
||||
results = await asyncio.gather(
|
||||
*[
|
||||
self.make_request(method, u, headers, doc, timeout)
|
||||
for u, doc in zip(urls, records)
|
||||
]
|
||||
)
|
||||
return results
|
||||
13
src/backend/langflow/components/helpers/CustomComponent.py
Normal file
13
src/backend/langflow/components/helpers/CustomComponent.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from langflow import CustomComponent
|
||||
from langflow.field_typing import Data
|
||||
|
||||
|
||||
class Component(CustomComponent):
|
||||
documentation: str = "http://docs.langflow.org/components/custom"
|
||||
icon = "custom_components"
|
||||
|
||||
def build_config(self):
|
||||
return {"param": {"display_name": "Parameter"}}
|
||||
|
||||
def build(self, param: Data) -> Data:
|
||||
return param
|
||||
22
src/backend/langflow/components/helpers/DocumentToRecord.py
Normal file
22
src/backend/langflow/components/helpers/DocumentToRecord.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
from typing import List
|
||||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class DocumentToRecordComponent(CustomComponent):
|
||||
display_name = "Documents to Records"
|
||||
description = "Convert documents to records."
|
||||
|
||||
field_config = {
|
||||
"documents": {"display_name": "Documents"},
|
||||
}
|
||||
|
||||
def build(self, documents: List[Document]) -> List[Record]:
|
||||
if isinstance(documents, Document):
|
||||
documents = [documents]
|
||||
records = [Record.from_document(document) for document in documents]
|
||||
self.status = records
|
||||
return records
|
||||
20
src/backend/langflow/components/helpers/GetNotified.py
Normal file
20
src/backend/langflow/components/helpers/GetNotified.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class GetNotifiedComponent(CustomComponent):
|
||||
display_name = "Get Notified"
|
||||
description = "A component to get notified by Notify component."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"name": {
|
||||
"display_name": "Name",
|
||||
"info": "The name of the notification to listen for.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, name: str) -> Record:
|
||||
state = self.get_state(name)
|
||||
self.status = state
|
||||
return state
|
||||
19
src/backend/langflow/components/helpers/IDGenerator.py
Normal file
19
src/backend/langflow/components/helpers/IDGenerator.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import uuid
|
||||
from typing import Text
|
||||
|
||||
from langflow import CustomComponent
|
||||
|
||||
|
||||
class UUIDGeneratorComponent(CustomComponent):
|
||||
documentation: str = "http://docs.langflow.org/components/custom"
|
||||
display_name = "Unique ID Generator"
|
||||
description = "Generates a unique ID."
|
||||
|
||||
def generate(self, *args, **kwargs):
|
||||
return Text(uuid.uuid4().hex)
|
||||
|
||||
def build_config(self):
|
||||
return {"unique_id": {"display_name": "Value", "value": self.generate}}
|
||||
|
||||
def build(self, unique_id: str) -> str:
|
||||
return unique_id
|
||||
19
src/backend/langflow/components/helpers/ListFlows.py
Normal file
19
src/backend/langflow/components/helpers/ListFlows.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from typing import List
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class ListFlowsComponent(CustomComponent):
|
||||
display_name = "List Flows"
|
||||
description = "A component to list all available flows."
|
||||
|
||||
def build_config(self):
|
||||
return {}
|
||||
|
||||
def build(
|
||||
self,
|
||||
) -> List[Record]:
|
||||
flows = self.list_flows()
|
||||
self.status = flows
|
||||
return flows
|
||||
41
src/backend/langflow/components/helpers/Notify.py
Normal file
41
src/backend/langflow/components/helpers/Notify.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class NotifyComponent(CustomComponent):
|
||||
display_name = "Notify"
|
||||
description = "A component to generate a notification to Get Notified component."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"name": {"display_name": "Name", "info": "The name of the notification."},
|
||||
"record": {"display_name": "Record", "info": "The record to store."},
|
||||
"append": {
|
||||
"display_name": "Append",
|
||||
"info": "If True, the record will be appended to the notification.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self, name: str, record: Optional[Record] = None, append: bool = False
|
||||
) -> Record:
|
||||
if record and not isinstance(record, Record):
|
||||
if isinstance(record, str):
|
||||
record = Record(text=record)
|
||||
elif isinstance(record, dict):
|
||||
record = Record(data=record)
|
||||
else:
|
||||
record = Record(text=str(record))
|
||||
elif not record:
|
||||
record = Record(text="")
|
||||
if record:
|
||||
if append:
|
||||
self.append_state(name, record)
|
||||
else:
|
||||
self.update_state(name, record)
|
||||
else:
|
||||
self.status = "No record provided."
|
||||
self.status = record
|
||||
return record
|
||||
24
src/backend/langflow/components/helpers/PythonFunction.py
Normal file
24
src/backend/langflow/components/helpers/PythonFunction.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from typing import Callable
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.field_typing import Code
|
||||
from langflow.interface.custom.utils import get_function
|
||||
|
||||
|
||||
class PythonFunctionComponent(CustomComponent):
|
||||
display_name = "Python Function"
|
||||
description = "Define a Python function."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"function_code": {
|
||||
"display_name": "Code",
|
||||
"info": "The code for the function.",
|
||||
"show": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, function_code: Code) -> Callable:
|
||||
self.status = function_code
|
||||
func = get_function(function_code)
|
||||
return func
|
||||
35
src/backend/langflow/components/helpers/RecordsAsText.py
Normal file
35
src/backend/langflow/components/helpers/RecordsAsText.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
from langflow import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class RecordsAsTextComponent(CustomComponent):
|
||||
display_name = "Records to Text"
|
||||
description = "Converts Records a list of Records to text using a template."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"records": {
|
||||
"display_name": "Records",
|
||||
"info": "The records to convert to text.",
|
||||
},
|
||||
"template": {
|
||||
"display_name": "Template",
|
||||
"info": "The template to use for formatting the records. It must contain the keys {text} and {data}.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
records: list[Record],
|
||||
template: str = "Text: {text}\nData: {data}",
|
||||
) -> Text:
|
||||
if not records:
|
||||
return ""
|
||||
if isinstance(records, Record):
|
||||
records = [records]
|
||||
|
||||
result_string = records_to_text(template, records)
|
||||
self.status = result_string
|
||||
return result_string
|
||||
58
src/backend/langflow/components/helpers/RunFlow.py
Normal file
58
src/backend/langflow/components/helpers/RunFlow.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.field_typing import NestedDict, Text
|
||||
from langflow.graph.schema import ResultData
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class RunFlowComponent(CustomComponent):
|
||||
display_name = "Run Flow"
|
||||
description = "A component to run a flow."
|
||||
|
||||
def get_flow_names(self) -> List[str]:
|
||||
flow_records = self.list_flows()
|
||||
return [flow_record.data["name"] for flow_record in flow_records]
|
||||
|
||||
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": self.get_flow_names,
|
||||
},
|
||||
"tweaks": {
|
||||
"display_name": "Tweaks",
|
||||
"info": "Tweaks to apply to the flow.",
|
||||
},
|
||||
}
|
||||
|
||||
def build_records_from_result_data(self, result_data: ResultData) -> Record:
|
||||
messages = result_data.messages
|
||||
records = []
|
||||
for message in messages:
|
||||
record = Record(text=message.get("text", ""), data={"result": result_data})
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
async def build(
|
||||
self, input_value: Text, flow_name: str, tweaks: NestedDict
|
||||
) -> Record:
|
||||
|
||||
results: List[Optional[ResultData]] = await self.run_flow(
|
||||
input_value=input_value, flow_name=flow_name, tweaks=tweaks
|
||||
)
|
||||
if isinstance(results, list):
|
||||
records = []
|
||||
for result in results:
|
||||
if result:
|
||||
records.extend(self.build_records_from_result_data(result))
|
||||
else:
|
||||
records = self.build_records_from_result_data(results)
|
||||
|
||||
self.status = records
|
||||
return records
|
||||
42
src/backend/langflow/components/helpers/RunnableExecutor.py
Normal file
42
src/backend/langflow/components/helpers/RunnableExecutor.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from langchain_core.runnables import Runnable
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class RunnableExecComponent(CustomComponent):
|
||||
documentation: str = "http://docs.langflow.org/components/custom"
|
||||
display_name = "Runnable Executor"
|
||||
beta = True
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_key": {
|
||||
"display_name": "Input Key",
|
||||
"info": "The key to use for the input.",
|
||||
},
|
||||
"input_value": {
|
||||
"display_name": "Inputs",
|
||||
"info": "The inputs to pass to the runnable.",
|
||||
},
|
||||
"runnable": {
|
||||
"display_name": "Runnable",
|
||||
"info": "The runnable to execute.",
|
||||
},
|
||||
"output_key": {
|
||||
"display_name": "Output Key",
|
||||
"info": "The key to use for the output.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_key: str,
|
||||
input_value: Text,
|
||||
runnable: Runnable,
|
||||
output_key: str = "output",
|
||||
) -> Text:
|
||||
result = runnable.invoke({input_key: input_value})
|
||||
result = result.get(output_key)
|
||||
self.status = result
|
||||
return result
|
||||
56
src/backend/langflow/components/helpers/SQLExecutor.py
Normal file
56
src/backend/langflow/components/helpers/SQLExecutor.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool
|
||||
from langchain_experimental.sql.base import SQLDatabase
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class SQLExecutorComponent(CustomComponent):
|
||||
display_name = "SQL Executor"
|
||||
description = "Execute SQL query."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"database": {"display_name": "Database"},
|
||||
"include_columns": {
|
||||
"display_name": "Include Columns",
|
||||
"info": "Include columns in the result.",
|
||||
},
|
||||
"passthrough": {
|
||||
"display_name": "Passthrough",
|
||||
"info": "If an error occurs, return the query instead of raising an exception.",
|
||||
},
|
||||
"add_error": {
|
||||
"display_name": "Add Error",
|
||||
"info": "Add the error to the result.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
query: str,
|
||||
database: SQLDatabase,
|
||||
include_columns: bool = False,
|
||||
passthrough: bool = False,
|
||||
add_error: bool = False,
|
||||
) -> Text:
|
||||
error = None
|
||||
try:
|
||||
tool = QuerySQLDataBaseTool(db=database)
|
||||
result = tool.run(query, include_columns=include_columns)
|
||||
self.status = result
|
||||
except Exception as e:
|
||||
result = Text(e)
|
||||
self.status = result
|
||||
if not passthrough:
|
||||
raise e
|
||||
error = repr(e)
|
||||
|
||||
if add_error and error is not None:
|
||||
result = f"{result}\n\nError: {error}\n\nQuery: {query}"
|
||||
elif error is not None:
|
||||
# Then we won't add the error to the result
|
||||
# but since we are in passthrough mode, we will return the query
|
||||
result = query
|
||||
|
||||
return result
|
||||
41
src/backend/langflow/components/helpers/SharedState.py
Normal file
41
src/backend/langflow/components/helpers/SharedState.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class SharedState(CustomComponent):
|
||||
display_name = "Shared State"
|
||||
description = "A component to share state between components."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"name": {"display_name": "Name", "info": "The name of the state."},
|
||||
"record": {"display_name": "Record", "info": "The record to store."},
|
||||
"append": {
|
||||
"display_name": "Append",
|
||||
"info": "If True, the record will be appended to the state.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self, name: str, record: Optional[Record] = None, append: bool = False
|
||||
) -> Record:
|
||||
if record:
|
||||
if append:
|
||||
self.append_state(name, record)
|
||||
else:
|
||||
self.update_state(name, record)
|
||||
|
||||
state = self.get_state(name)
|
||||
if state and not isinstance(state, Record):
|
||||
if isinstance(state, str):
|
||||
state = Record(text=state)
|
||||
elif isinstance(state, dict):
|
||||
state = Record(data=state)
|
||||
else:
|
||||
state = Record(text=str(state))
|
||||
elif not state:
|
||||
state = Record(text="")
|
||||
self.status = state
|
||||
return state
|
||||
49
src/backend/langflow/components/helpers/ShouldRunNext.py
Normal file
49
src/backend/langflow/components/helpers/ShouldRunNext.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Implement ShouldRunNext component
|
||||
from typing import Text
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Prompt
|
||||
|
||||
|
||||
class ShouldRunNext(CustomComponent):
|
||||
display_name = "Should Run Next"
|
||||
description = "Decides whether to run the next component."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"prompt": {
|
||||
"display_name": "Prompt",
|
||||
"info": "The prompt to use for the decision. It should generate a boolean response (True or False).",
|
||||
},
|
||||
"llm": {
|
||||
"display_name": "LLM",
|
||||
"info": "The language model to use for the decision.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, template: Prompt, llm: BaseLanguageModel, **kwargs) -> dict:
|
||||
# This is a simple component that always returns True
|
||||
prompt_template = PromptTemplate.from_template(Text(template))
|
||||
|
||||
attributes_to_check = ["text", "page_content"]
|
||||
for key, value in kwargs.items():
|
||||
for attribute in attributes_to_check:
|
||||
if hasattr(value, attribute):
|
||||
kwargs[key] = getattr(value, attribute)
|
||||
|
||||
chain = prompt_template | llm
|
||||
result = chain.invoke(kwargs)
|
||||
if hasattr(result, "content") and isinstance(result.content, str):
|
||||
result = result.content
|
||||
elif isinstance(result, str):
|
||||
result = result
|
||||
else:
|
||||
result = result.get("response")
|
||||
|
||||
if result.lower() not in ["true", "false"]:
|
||||
raise ValueError("The prompt should generate a boolean response (True or False).")
|
||||
# The string should be the words true or false
|
||||
# if not raise an error
|
||||
bool_result = result.lower() == "true"
|
||||
return {"condition": bool_result, "result": kwargs}
|
||||
31
src/backend/langflow/components/helpers/TextToRecord.py
Normal file
31
src/backend/langflow/components/helpers/TextToRecord.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class TextToRecordComponent(CustomComponent):
|
||||
display_name = "Text to Record"
|
||||
description = "Converts text to a Record."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"text": {
|
||||
"display_name": "Text",
|
||||
"info": "The text to convert to a record.",
|
||||
},
|
||||
"data": {
|
||||
"display_name": "Data",
|
||||
"info": "The optional data to include in the record.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
text: Text,
|
||||
data: Optional[dict] = {},
|
||||
) -> Record:
|
||||
record = Record(text=text, data=data)
|
||||
self.status = record
|
||||
return record
|
||||
0
src/backend/langflow/components/helpers/__init__.py
Normal file
0
src/backend/langflow/components/helpers/__init__.py
Normal file
|
|
@ -47,9 +47,7 @@ import {
|
|||
FileText,
|
||||
FileType2,
|
||||
FileUp,
|
||||
Fingerprint,
|
||||
FlaskConical,
|
||||
FolderOpen,
|
||||
FolderPlus,
|
||||
FormInput,
|
||||
Forward,
|
||||
|
|
@ -66,7 +64,6 @@ import {
|
|||
Key,
|
||||
Laptop2,
|
||||
Layers,
|
||||
Lightbulb,
|
||||
Link,
|
||||
Loader2,
|
||||
Lock,
|
||||
|
|
@ -94,7 +91,6 @@ import {
|
|||
Redo,
|
||||
RefreshCcw,
|
||||
Repeat,
|
||||
Rocket,
|
||||
Save,
|
||||
SaveAll,
|
||||
Scissors,
|
||||
|
|
@ -126,10 +122,8 @@ import {
|
|||
UserPlus2,
|
||||
Users2,
|
||||
Variable,
|
||||
Wand,
|
||||
Wand2,
|
||||
Workflow,
|
||||
Wrench,
|
||||
X,
|
||||
XCircle,
|
||||
Zap,
|
||||
|
|
@ -265,7 +259,7 @@ export const nodeNames: { [char: string]: string } = {
|
|||
wrappers: "Wrappers",
|
||||
textsplitters: "Text Splitters",
|
||||
retrievers: "Retrievers",
|
||||
utilities: "Helpers",
|
||||
helpers: "Helpers",
|
||||
langchain_utilities: "Utilities",
|
||||
output_parsers: "Output Parsers",
|
||||
custom_components: "Custom",
|
||||
|
|
|
|||
Loading…
Reference in a new issue