Merge branch 'zustand/io/migration' into new_project_modal

This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-03-05 21:17:03 -03:00
commit 306bdd9fd5
17 changed files with 1 additions and 7 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 into single piece of 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 can contain the keys {text}, {data} or any other key in the Record.",
},
}
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

View 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

View 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

View 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

View 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

View 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}

View 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

View file

@ -50,9 +50,7 @@ import {
FileText,
FileType2,
FileUp,
Fingerprint,
FlaskConical,
FolderOpen,
FolderPlus,
FormInput,
Forward,
@ -69,7 +67,6 @@ import {
Key,
Laptop2,
Layers,
Lightbulb,
Link,
Loader2,
Lock,
@ -97,7 +94,6 @@ import {
Redo,
RefreshCcw,
Repeat,
Rocket,
Save,
SaveAll,
Scissors,
@ -129,10 +125,8 @@ import {
UserPlus2,
Users2,
Variable,
Wand,
Wand2,
Workflow,
Wrench,
X,
XCircle,
Zap,
@ -268,7 +262,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",