merge fix

This commit is contained in:
cristhianzl 2024-03-29 17:52:09 -03:00
commit 3a612c874c
89 changed files with 1296 additions and 1059 deletions

View file

@ -26,13 +26,13 @@ def upgrade() -> None:
flow_constraints = inspector.get_unique_constraints("flow")
user_constraints = inspector.get_unique_constraints("user")
try:
if not any(constraint["name"] == "uq_apikey_id" for constraint in api_key_constraints):
if not any(constraint["column_names"] == ["id"] for constraint in api_key_constraints):
with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.create_unique_constraint("uq_apikey_id", ["id"])
if not any(constraint["name"] == "uq_flow_id" for constraint in flow_constraints):
if not any(constraint["column_names"] == ["id"] for constraint in flow_constraints):
with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.create_unique_constraint("uq_flow_id", ["id"])
if not any(constraint["name"] == "uq_user_id" for constraint in user_constraints):
if not any(constraint["column_names"] == ["id"] for constraint in user_constraints):
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.create_unique_constraint("uq_user_id", ["id"])
except Exception as e:

View file

@ -2,6 +2,7 @@ import warnings
from typing import Optional, Union
from langflow.field_typing import Text
from langflow.helpers.record import records_to_text
from langflow.interface.custom.custom_component import CustomComponent
from langflow.memory import add_messages
from langflow.schema import Record
@ -21,6 +22,7 @@ class ChatComponent(CustomComponent):
"sender": {
"options": ["Machine", "User"],
"display_name": "Sender Type",
"advanced": True,
},
"sender_name": {"display_name": "Sender Name"},
"session_id": {
@ -31,6 +33,13 @@ class ChatComponent(CustomComponent):
"return_record": {
"display_name": "Return Record",
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
"advanced": True,
},
"record_template": {
"display_name": "Record Template",
"multiline": True,
"info": "In case of Message being a Record, this template will be used to convert it to text.",
"advanced": True,
},
}
@ -74,9 +83,10 @@ class ChatComponent(CustomComponent):
self,
sender: Optional[str] = "User",
sender_name: Optional[str] = "User",
input_value: Optional[str] = None,
input_value: Optional[Union[str, Record]] = None,
session_id: Optional[str] = None,
return_record: Optional[bool] = False,
record_template: Optional[str] = "Text: {text}\nData: {data}",
) -> Union[Text, Record]:
input_value_record: Optional[Record] = None
if return_record:
@ -94,6 +104,8 @@ class ChatComponent(CustomComponent):
"session_id": session_id,
},
)
elif isinstance(input_value, Record):
input_value = records_to_text(template=record_template, records=input_value)
if not input_value:
input_value = ""
if return_record and input_value_record:

View file

@ -1,18 +1,28 @@
from typing import Optional
from typing import Optional, Union
from langflow.field_typing import Text
from langflow.helpers.record import records_to_text
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema.schema import Record
class TextComponent(CustomComponent):
display_name = "Text Component"
description = "Used to pass text to the next component."
field_config = {
"input_value": {"display_name": "Value", "multiline": True},
}
def build_config(self):
return {
"input_value": {"display_name": "Value", "input_types": ["Record"], "info": "Text or Record to be passed."},
"record_template": {"display_name": "Record Template", "multiline": True},
}
def build(self, input_value: Optional[str] = "") -> Text:
def build(
self,
input_value: Optional[Union[Text, Record]] = "",
record_template: Optional[str] = "Text: {text}\nData: {data}",
) -> Text:
if isinstance(input_value, Record):
input_value = records_to_text(template=record_template, records=input_value)
self.status = input_value
if not input_value:
input_value = ""

View file

@ -1,18 +1,17 @@
__all__ = [
"agents",
"chains",
"data",
"documentloaders",
"embeddings",
"experimental",
"helpers",
"inputs",
"memories",
"model_specs",
"models",
"outputs",
"retrievers",
"textsplitters",
"toolkits",
"tools",
"vectorsearch",
"vectorstores",
]

View file

@ -5,6 +5,7 @@ from .OpenAIConversationalAgent import ConversationalAgent
from .SQLAgent import SQLAgentComponent
from .VectorStoreAgent import VectorStoreAgentComponent
from .VectorStoreRouterAgent import VectorStoreRouterAgentComponent
from .XMLAgent import XMLAgentComponent
__all__ = [
"AgentInitializerComponent",
@ -14,4 +15,5 @@ __all__ = [
"SQLAgentComponent",
"VectorStoreAgentComponent",
"VectorStoreRouterAgentComponent",
"XMLAgentComponent",
]

View file

@ -10,11 +10,11 @@ from langflow.schema import Record
class APIRequest(CustomComponent):
display_name: str = "API Request"
description: str = "Make an HTTP request to the given URL."
description: str = "Make HTTP requests given one or more URLs."
output_types: list[str] = ["Record"]
documentation: str = "https://docs.langflow.org/components/utilities#api-request"
field_config = {
"urls": {"display_name": "URLs", "info": "The URLs to make the request to."},
"urls": {"display_name": "URLs", "info": "URLs to make requests to."},
"method": {
"display_name": "Method",
"info": "The HTTP method to use.",

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class DirectoryComponent(CustomComponent):
display_name = "Directory"
description = "Load Text Files from a Directory and Convert Them to Records."
description = "Recursively load files from a directory."
def build_config(self) -> Dict[str, Any]:
return {

View file

@ -1,4 +1,5 @@
from typing import Any, Dict, List
from pathlib import Path
from typing import Any, Dict
from langflow.base.data.utils import TEXT_FILE_TYPES, parse_text_file_to_record
from langflow.interface.custom.custom_component import CustomComponent
@ -7,12 +8,12 @@ from langflow.schema import Record
class FileComponent(CustomComponent):
display_name = "Files"
description = "Read Text Files"
description = "A generic file loader."
def build_config(self) -> Dict[str, Any]:
return {
"paths": {
"display_name": "Paths",
"path": {
"display_name": "Path",
"field_type": "file",
"file_types": TEXT_FILE_TYPES,
"info": f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
@ -26,7 +27,8 @@ class FileComponent(CustomComponent):
def load_file(self, path: str, silent_errors: bool = False) -> Record:
resolved_path = self.resolve_path(path)
extension = resolved_path.split(".")[-1]
path_obj = Path(resolved_path)
extension = path_obj.suffix[1:].lower()
if extension == "doc":
raise ValueError("doc files are not supported. Please save as .docx")
if extension not in TEXT_FILE_TYPES:
@ -37,9 +39,9 @@ class FileComponent(CustomComponent):
def build(
self,
paths: List[str],
path: str,
silent_errors: bool = False,
) -> List[Record]:
records = [self.load_file(path, silent_errors) for path in paths]
self.status = records
return records
) -> Record:
record = self.load_file(path, silent_errors)
self.status = record
return record

View file

@ -1,117 +0,0 @@
from typing import List
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema import Record
from langflow.utils.constants import LOADERS_INFO
class FileLoaderComponent(CustomComponent):
display_name: str = "File Loader"
description: str = "Generic File Loader"
def build_config(self):
loader_options = ["Automatic"] + [loader_info["name"] for loader_info in LOADERS_INFO]
file_types = []
suffixes = []
for loader_info in LOADERS_INFO:
if "allowedTypes" in loader_info:
file_types.extend(loader_info["allowedTypes"])
suffixes.extend([f".{ext}" for ext in loader_info["allowedTypes"]])
return {
"file_path": {
"display_name": "File Path",
"required": True,
"field_type": "file",
"file_types": [
"json",
"txt",
"csv",
"jsonl",
"html",
"htm",
"conllu",
"enex",
"msg",
"pdf",
"srt",
"eml",
"md",
"mdx",
"pptx",
"docx",
],
"suffixes": [
".json",
".txt",
".csv",
".jsonl",
".html",
".htm",
".conllu",
".enex",
".msg",
".pdf",
".srt",
".eml",
".md",
".mdx",
".pptx",
".docx",
],
# "file_types" : file_types,
# "suffixes": suffixes,
},
"loader": {
"display_name": "Loader",
"is_list": True,
"required": True,
"options": loader_options,
"value": "Automatic",
},
"code": {"show": False},
}
def build(self, file_path: str, loader: str) -> List[Record]:
file_type = file_path.split(".")[-1]
# Map the loader to the correct loader class
selected_loader_info = None
for loader_info in LOADERS_INFO:
if loader_info["name"] == loader:
selected_loader_info = loader_info
break
if selected_loader_info is None and loader != "Automatic":
raise ValueError(f"Loader {loader} not found in the loader info list")
if loader == "Automatic":
# Determine the loader based on the file type
default_loader_info = None
for info in LOADERS_INFO:
if "defaultFor" in info and file_type in info["defaultFor"]:
default_loader_info = info
break
if default_loader_info is None:
raise ValueError(f"No default loader found for file type: {file_type}")
selected_loader_info = default_loader_info
if isinstance(selected_loader_info, dict):
loader_import: str = selected_loader_info["import"]
else:
raise ValueError(f"Loader info for {loader} is not a dict\nLoader info:\n{selected_loader_info}")
module_name, class_name = loader_import.rsplit(".", 1)
try:
# Import the loader class
loader_module = __import__(module_name, fromlist=[class_name])
loader_instance = getattr(loader_module, class_name)
except ImportError as e:
raise ValueError(f"Loader {loader} could not be imported\nLoader info:\n{selected_loader_info}") from e
result = loader_instance(file_path=file_path)
docs = result.load()
return self.to_records(docs)

View file

@ -8,7 +8,7 @@ from langflow.schema import Record
class URLComponent(CustomComponent):
display_name = "URL"
description = "Load URLs and convert them to records."
description = "Fetch text content given one or more URLs."
def build_config(self) -> Dict[str, Any]:
return {
@ -22,4 +22,5 @@ class URLComponent(CustomComponent):
loader = WebBaseLoader(web_paths=urls)
docs = loader.load()
records = self.to_records(docs)
self.status = records
return records

View file

@ -1,7 +1,7 @@
from .APIRequest import APIRequest
from .Directory import DirectoryComponent
from .File import FileComponent
from .FileLoader import FileLoaderComponent
from .URL import URLComponent
__all__ = ["APIRequest", "DirectoryComponent", "FileComponent", "FileLoaderComponent", "URLComponent"]
__all__ = ["APIRequest", "DirectoryComponent", "FileComponent", "URLComponent"]

View file

@ -2,7 +2,7 @@ from typing import Optional
from langchain_community.embeddings.cohere import CohereEmbeddings
from langflow.interface.custom.custom_component import CustomComponent
from langflow.custom import CustomComponent
class CohereEmbeddingsComponent(CustomComponent):

View file

@ -1,23 +1,27 @@
from .ClearMessageHistory import ClearMessageHistoryComponent
from .ExtractDataFromRecord import ExtractKeyFromRecordComponent
from .Listen import ListenComponent
from .FlowTool import FlowToolComponent
from .ListFlows import ListFlowsComponent
from .Listen import ListenComponent
from .MergeRecords import MergeRecordsComponent
from .Notify import NotifyComponent
from .PythonFunction import PythonFunctionComponent
from .RunFlow import RunFlowComponent
from .RunnableExecutor import RunnableExecComponent
from .SQLExecutor import SQLExecutorComponent
from .SubFlow import SubFlowComponent
__all__ = [
"ClearMessageHistoryComponent",
"ExtractKeyFromRecordComponent",
"ListenComponent",
"FlowToolComponent",
"ListFlowsComponent",
"ListenComponent",
"MergeRecordsComponent",
"MessageHistoryComponent",
"NotifyComponent",
"PythonFunctionComponent",
"RunFlowComponent",
"RunnableExecComponent",
"SQLExecutorComponent",
"TextToRecordComponent",
"SubFlowComponent",
]

View file

@ -2,16 +2,16 @@ from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema import Record
class TextToRecordComponent(CustomComponent):
display_name = "Text to Record"
description = "A component to create a record from Text."
class CreateRecordComponent(CustomComponent):
display_name = "Create Record"
description = "A component to create a record."
beta: bool = True
def build_config(self):
return {
"data": {
"display_name": "Data",
"info": "The data to convert to a record.",
"info": "Data to contruct the record.",
"input_types": ["Text"],
}
}

View file

@ -7,8 +7,8 @@ from langflow.schema import Record
class DocumentToRecordComponent(CustomComponent):
display_name = "Documents to Records"
description = "Convert documents to records."
display_name = "Documents To Records"
description = "Convert LangChain Documents into Records."
field_config = {
"documents": {"display_name": "Documents"},

View file

@ -6,7 +6,7 @@ from langflow.interface.custom.custom_component import CustomComponent
class UUIDGeneratorComponent(CustomComponent):
documentation: str = "http://docs.langflow.org/components/custom"
display_name = "Unique ID Generator"
display_name = "ID Generator"
description = "Generates a unique ID."
def update_build_config(

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class MessageHistoryComponent(CustomComponent):
display_name = "Message History"
description = "Used to retrieve stored messages."
description = "Used to retrieve stored chat messages."
beta: bool = True
def build_config(self):
@ -30,7 +30,7 @@ class MessageHistoryComponent(CustomComponent):
def build(
self,
sender: Optional[str] = None,
sender: Optional[str] = "Machine",
sender_name: Optional[str] = None,
session_id: Optional[str] = None,
n_messages: int = 5,

View file

@ -4,9 +4,9 @@ from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema import Record
class RecordsAsTextComponent(CustomComponent):
display_name = "Records to Text"
description = "Converts Records into single piece of text using a template."
class RecordsToTextComponent(CustomComponent):
display_name = "Records To Text"
description = "Convert Records into plain text using a pre-defined template."
def build_config(self):
return {

View file

@ -4,7 +4,7 @@ from langflow.schema import Record
class UpdateRecordComponent(CustomComponent):
display_name = "Update Record"
description = "Updates a record with new data."
description = "Updates a Record with new data. Akin to a Python dictionary update."
def build_config(self):
return {

View file

@ -2,8 +2,6 @@ from .CustomComponent import Component
from .DocumentToRecord import DocumentToRecordComponent
from .IDGenerator import UUIDGeneratorComponent
from .MessageHistory import MessageHistoryComponent
from .PythonFunction import PythonFunctionComponent
from .RecordsAsText import RecordsAsTextComponent
from .TextToRecord import TextToRecordComponent
from .UpdateRecord import UpdateRecordComponent
@ -13,7 +11,7 @@ __all__ = [
"DocumentToRecordComponent",
"UUIDGeneratorComponent",
"PythonFunctionComponent",
"RecordsAsTextComponent",
"RecordsToTextComponent",
"TextToRecordComponent",
"MessageHistoryComponent",
]

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class ChatInput(ChatComponent):
display_name = "Chat Input"
description = "Used to get user input from the chat."
description = "Capture user inputs from the chat interface."
icon = "ChatInput"
def build(

View file

@ -1,30 +0,0 @@
import ast
import json
from langflow.custom import CustomComponent
from langflow.schema import Record
class JSONInputComponent(CustomComponent):
display_name = "JSON Input"
description = "Load a JSON object as input."
def build_config(self):
return {
"json_str": {
"display_name": "JSON String",
"multiline": True,
"info": "The JSON string to load.",
}
}
def build(self, json_str: str) -> Record:
try:
data = json.loads(json_str)
except json.JSONDecodeError:
try:
data = ast.literal_eval(json_str)
except (SyntaxError, ValueError):
raise ValueError("Invalid JSON string.")
record = Record(data=data)
return record

View file

@ -6,7 +6,7 @@ from langflow.interface.custom.custom_component import CustomComponent
class PromptComponent(CustomComponent):
display_name: str = "Prompt"
description: str = "A component for creating prompts using templates"
description: str = "A component for creating prompt templates using dynamic variables."
icon = "terminal-square"
def build_config(self):

View file

@ -6,7 +6,21 @@ from langflow.field_typing import Text
class TextInput(TextComponent):
display_name = "Text Input"
description = "Used to pass text input to the next component."
description = "Capture Text or Record and send text inputs."
def build(self, input_value: Optional[str] = "") -> Text:
return super().build(input_value=input_value)
def build_config(self):
return {
"input_value": {
"display_name": "Value",
"input_types": ["Record"],
"info": "Text or Record to be passed as input.",
},
"record_template": {"display_name": "Record Template", "multiline": True},
}
def build(
self,
input_value: Optional[str] = "",
record_template: Optional[str] = "{text}",
) -> Text:
return super().build(input_value=input_value, record_template=record_template)

View file

@ -2,4 +2,4 @@ from .ChatInput import ChatInput
from .Prompt import PromptComponent
from .TextInput import TextInput
__all__ = ["ChatInput", "TextInput", "PromptComponent"]
__all__ = ["ChatInput", "PromptComponent", "TextInput"]

View file

@ -1,33 +0,0 @@
from typing import Dict, Optional
from langchain_community.llms.ctransformers import CTransformers
from langflow.interface.custom.custom_component import CustomComponent
class CTransformersComponent(CustomComponent):
display_name = "CTransformers"
description = "C Transformers LLM models"
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
def build_config(self):
return {
"model": {"display_name": "Model", "required": True},
"model_file": {
"display_name": "Model File",
"required": False,
"field_type": "file",
"file_types": [".bin"],
},
"model_type": {"display_name": "Model Type", "required": True},
"config": {
"display_name": "Config",
"advanced": True,
"required": False,
"field_type": "dict",
"value": '{"top_k":40,"top_p":0.95,"temperature":0.8,"repetition_penalty":1.1,"last_n_tokens":64,"seed":-1,"max_new_tokens":256,"stop":"","stream":"False","reset":"True","batch_size":8,"threads":-1,"context_length":-1,"gpu_layers":0}',
},
}
def build(self, model: str, model_file: str, model_type: str, config: Optional[Dict] = None) -> CTransformers:
return CTransformers(model=model, model_file=model_file, model_type=model_type, config=config) # type: ignore

View file

@ -3,8 +3,8 @@ from typing import Callable, Optional, Union
from langchain_community.chat_models.anthropic import ChatAnthropic
from pydantic.v1.types import SecretStr
from langflow.custom import CustomComponent
from langflow.field_typing import BaseLanguageModel
from langflow.interface.custom.custom_component import CustomComponent
class ChatAnthropicComponent(CustomComponent):
@ -20,31 +20,48 @@ class ChatAnthropicComponent(CustomComponent):
"field_type": "str",
"password": True,
},
"anthropic_api_url": {
"display_name": "Anthropic API URL",
"field_type": "str",
},
"model_kwargs": {
"display_name": "Model Kwargs",
"field_type": "dict",
"advanced": True,
},
"model_name": {
"display_name": "Model Name",
"field_type": "str",
"advanced": False,
"required": False,
"options": ["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"],
},
"temperature": {
"display_name": "Temperature",
"field_type": "float",
},
"max_tokens": {
"display_name": "Max Tokens",
"field_type": "int",
"advanced": False,
"required": False,
},
"top_k": {"display_name": "Top K", "field_type": "int", "advanced": True},
"top_p": {"display_name": "Top P", "field_type": "float", "advanced": True},
}
def build(
self,
anthropic_api_key: str,
anthropic_api_url: Optional[str] = None,
model_kwargs: dict = {},
model_name: str = "claude-3-opus-20240229",
temperature: Optional[float] = None,
max_tokens: Optional[int] = 1024,
top_k: Optional[int] = None,
top_p: Optional[float] = None,
) -> Union[BaseLanguageModel, Callable]:
return ChatAnthropic(
anthropic_api_key=SecretStr(anthropic_api_key),
anthropic_api_url=anthropic_api_url,
model_kwargs=model_kwargs,
model_name=model_name,
temperature=temperature,
max_tokens=max_tokens, # type: ignore
top_k=top_k,
top_p=top_p,
)

View file

@ -1,131 +0,0 @@
from typing import Any, Dict, List, Optional
from langchain_community.llms.llamacpp import LlamaCpp
from langflow.interface.custom.custom_component import CustomComponent
class LlamaCppComponent(CustomComponent):
display_name = "LlamaCpp"
description = "llama.cpp model."
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
def build_config(self):
return {
"grammar": {"display_name": "Grammar", "advanced": True},
"cache": {"display_name": "Cache", "advanced": True},
"client": {"display_name": "Client", "advanced": True},
"echo": {"display_name": "Echo", "advanced": True},
"f16_kv": {"display_name": "F16 KV", "advanced": True},
"grammar_path": {"display_name": "Grammar Path", "advanced": True},
"last_n_tokens_size": {"display_name": "Last N Tokens Size", "advanced": True},
"logits_all": {"display_name": "Logits All", "advanced": True},
"logprobs": {"display_name": "Logprobs", "advanced": True},
"lora_base": {"display_name": "Lora Base", "advanced": True},
"lora_path": {"display_name": "Lora Path", "advanced": True},
"max_tokens": {"display_name": "Max Tokens", "advanced": True},
"metadata": {"display_name": "Metadata", "advanced": True},
"model_kwargs": {"display_name": "Model Kwargs", "advanced": True},
"model_path": {
"display_name": "Model Path",
"field_type": "file",
"file_types": [".bin"],
"required": True,
},
"n_batch": {"display_name": "N Batch", "advanced": True},
"n_ctx": {"display_name": "N Ctx", "advanced": True},
"n_gpu_layers": {"display_name": "N GPU Layers", "advanced": True},
"n_parts": {"display_name": "N Parts", "advanced": True},
"n_threads": {"display_name": "N Threads", "advanced": True},
"repeat_penalty": {"display_name": "Repeat Penalty", "advanced": True},
"rope_freq_base": {"display_name": "Rope Freq Base", "advanced": True},
"rope_freq_scale": {"display_name": "Rope Freq Scale", "advanced": True},
"seed": {"display_name": "Seed", "advanced": True},
"stop": {"display_name": "Stop", "advanced": True},
"streaming": {"display_name": "Streaming", "advanced": True},
"suffix": {"display_name": "Suffix", "advanced": True},
"tags": {"display_name": "Tags", "advanced": True},
"temperature": {"display_name": "Temperature"},
"top_k": {"display_name": "Top K", "advanced": True},
"top_p": {"display_name": "Top P", "advanced": True},
"use_mlock": {"display_name": "Use Mlock", "advanced": True},
"use_mmap": {"display_name": "Use Mmap", "advanced": True},
"verbose": {"display_name": "Verbose", "advanced": True},
"vocab_only": {"display_name": "Vocab Only", "advanced": True},
}
def build(
self,
model_path: str,
grammar: Optional[str] = None,
cache: Optional[bool] = None,
client: Optional[Any] = None,
echo: Optional[bool] = False,
f16_kv: bool = True,
grammar_path: Optional[str] = None,
last_n_tokens_size: Optional[int] = 64,
logits_all: bool = False,
logprobs: Optional[int] = None,
lora_base: Optional[str] = None,
lora_path: Optional[str] = None,
max_tokens: Optional[int] = 256,
metadata: Optional[Dict] = None,
model_kwargs: Dict = {},
n_batch: Optional[int] = 8,
n_ctx: int = 512,
n_gpu_layers: Optional[int] = 1,
n_parts: int = -1,
n_threads: Optional[int] = 1,
repeat_penalty: Optional[float] = 1.1,
rope_freq_base: float = 10000.0,
rope_freq_scale: float = 1.0,
seed: int = -1,
stop: Optional[List[str]] = [],
streaming: bool = True,
suffix: Optional[str] = "",
tags: Optional[List[str]] = [],
temperature: Optional[float] = 0.8,
top_k: Optional[int] = 40,
top_p: Optional[float] = 0.95,
use_mlock: bool = False,
use_mmap: Optional[bool] = True,
verbose: bool = True,
vocab_only: bool = False,
) -> LlamaCpp:
return LlamaCpp(
model_path=model_path,
grammar=grammar,
cache=cache,
client=client,
echo=echo,
f16_kv=f16_kv,
grammar_path=grammar_path,
last_n_tokens_size=last_n_tokens_size,
logits_all=logits_all,
logprobs=logprobs,
lora_base=lora_base,
lora_path=lora_path,
max_tokens=max_tokens,
metadata=metadata,
model_kwargs=model_kwargs,
n_batch=n_batch,
n_ctx=n_ctx,
n_gpu_layers=n_gpu_layers,
n_parts=n_parts,
n_threads=n_threads,
repeat_penalty=repeat_penalty,
rope_freq_base=rope_freq_base,
rope_freq_scale=rope_freq_scale,
seed=seed,
stop=stop,
streaming=streaming,
suffix=suffix,
tags=tags,
temperature=temperature,
top_k=top_k,
top_p=top_p,
use_mlock=use_mlock,
use_mmap=use_mmap,
verbose=verbose,
vocab_only=vocab_only,
)

View file

@ -4,7 +4,6 @@ from .AnthropicSpecs import AnthropicComponent
from .AzureChatOpenAISpecs import AzureChatOpenAISpecsComponent
from .BaiduQianfanChatEndpointsSpecs import QianfanChatEndpointComponent
from .BaiduQianfanLLMEndpointsSpecs import QianfanLLMEndpointComponent
from .CTransformersSpecs import CTransformersComponent
from .ChatAnthropicSpecs import ChatAnthropicComponent
from .ChatLiteLLMSpecs import ChatLiteLLMComponent
from .ChatOllamaEndpointSpecs import ChatOllamaComponent
@ -13,7 +12,6 @@ from .ChatVertexAISpecs import ChatVertexAIComponent
from .CohereSpecs import CohereComponent
from .GoogleGenerativeAISpecs import GoogleGenerativeAIComponent
from .HuggingFaceEndpointsSpecs import HuggingFaceEndpointsComponent
from .LlamaCppSpecs import LlamaCppComponent
from .OllamaLLMSpecs import OllamaLLM
from .VertexAISpecs import VertexAIComponent
@ -24,7 +22,6 @@ __all__ = [
"AzureChatOpenAISpecsComponent",
"QianfanChatEndpointComponent",
"QianfanLLMEndpointComponent",
"CTransformersComponent",
"ChatAnthropicComponent",
"ChatLiteLLMComponent",
"ChatOllamaComponent",
@ -33,7 +30,6 @@ __all__ = [
"CohereComponent",
"GoogleGenerativeAIComponent",
"HuggingFaceEndpointsComponent",
"LlamaCppComponent",
"OllamaLLM",
"VertexAIComponent",
]

View file

@ -10,31 +10,50 @@ class AmazonBedrockComponent(LCModelComponent):
display_name: str = "Amazon Bedrock"
description: str = "Generate text using LLM model from Amazon Bedrock."
icon = "Amazon"
field_order = [
"model_id",
"credentials_profile_name",
"region_name",
"model_kwargs",
"endpoint_url",
"cache",
"stream",
"input_value",
"system_message",
]
def build_config(self):
return {
"model_id": {
"display_name": "Model Id",
"options": [
"ai21.j2-grande-instruct",
"ai21.j2-jumbo-instruct",
"ai21.j2-mid",
"ai21.j2-mid-v1",
"ai21.j2-ultra",
"ai21.j2-ultra-v1",
"anthropic.claude-instant-v1",
"anthropic.claude-v1",
"amazon.titan-text-express-v1",
"amazon.titan-text-lite-v1",
"amazon.titan-embed-text-v1",
"amazon.titan-embed-image-v1",
"amazon.titan-image-generator-v1",
"anthropic.claude-v2",
"anthropic.claude-v2:1",
"anthropic.claude-3-sonnet-20240229-v1:0",
"anthropic.claude-3-haiku-20240307-v1:0",
"anthropic.claude-instant-v1",
"ai21.j2-mid-v1",
"ai21.j2-ultra-v1",
"cohere.command-text-v14",
"cohere.command-light-text-v14",
"cohere.embed-english-v3",
"cohere.embed-multilingual-v3",
"meta.llama2-13b-chat-v1",
"meta.llama2-70b-chat-v1",
"mistral.mistral-7b-instruct-v0:2",
"mistral.mixtral-8x7b-instruct-v0:1",
],
},
"credentials_profile_name": {"display_name": "Credentials Profile Name"},
"streaming": {"display_name": "Streaming", "field_type": "bool"},
"endpoint_url": {"display_name": "Endpoint URL"},
"region_name": {"display_name": "Region Name"},
"model_kwargs": {"display_name": "Model Kwargs"},
"cache": {"display_name": "Cache"},
"code": {"advanced": True},
"input_value": {"display_name": "Input"},
"system_message": {"display_name": "System Message", "info": "System message to pass to the model."},
"stream": {
@ -52,7 +71,6 @@ class AmazonBedrockComponent(LCModelComponent):
region_name: Optional[str] = None,
model_kwargs: Optional[dict] = None,
endpoint_url: Optional[str] = None,
streaming: bool = False,
cache: Optional[bool] = None,
stream: bool = False,
) -> Text:
@ -63,7 +81,7 @@ class AmazonBedrockComponent(LCModelComponent):
region_name=region_name,
model_kwargs=model_kwargs,
endpoint_url=endpoint_url,
streaming=streaming,
streaming=stream,
cache=cache,
) # type: ignore
except Exception as e:

View file

@ -17,15 +17,17 @@ class AnthropicLLM(LCModelComponent):
"model": {
"display_name": "Model Name",
"options": [
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
"claude-2.1",
"claude-2.0",
"claude-instant-1.2",
"claude-instant-1",
# Add more models as needed
],
"info": "https://python.langchain.com/docs/integrations/chat/anthropic",
"required": True,
"value": "claude-2.1",
"value": "claude-3-opus-20240229",
},
"anthropic_api_key": {
"display_name": "Anthropic API Key",
@ -43,8 +45,8 @@ class AnthropicLLM(LCModelComponent):
"field_type": "float",
"value": 0.7,
},
"api_endpoint": {
"display_name": "API Endpoint",
"anthropic_api_url": {
"display_name": "Anthropic API URL",
"info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.",
},
"code": {"show": False},
@ -67,12 +69,12 @@ class AnthropicLLM(LCModelComponent):
anthropic_api_key: Optional[str] = None,
max_tokens: Optional[int] = None,
temperature: Optional[float] = None,
api_endpoint: Optional[str] = None,
anthropic_api_url: Optional[str] = None,
stream: bool = False,
) -> Text:
# Set default API endpoint if not provided
if not api_endpoint:
api_endpoint = "https://api.anthropic.com"
if not anthropic_api_url:
anthropic_api_url = "https://api.anthropic.com"
try:
output = ChatAnthropic(
@ -80,7 +82,7 @@ class AnthropicLLM(LCModelComponent):
anthropic_api_key=(SecretStr(anthropic_api_key) if anthropic_api_key else None),
max_tokens_to_sample=max_tokens, # type: ignore
temperature=temperature,
anthropic_api_url=api_endpoint,
anthropic_api_url=anthropic_api_url,
)
except Exception as e:
raise ValueError("Could not connect to Anthropic API.") from e

View file

@ -8,7 +8,7 @@ from langflow.field_typing import Text
class AzureChatOpenAIComponent(LCModelComponent):
display_name: str = "AzureOpenAI"
display_name: str = "Azure OpenAI"
description: str = "Generate text using LLM model from Azure OpenAI."
documentation: str = "https://python.langchain.com/docs/integrations/llms/azure_openai"
beta = False

View file

@ -8,7 +8,7 @@ from langflow.field_typing import Text
class QianfanChatEndpointComponent(LCModelComponent):
display_name: str = "QianfanChat"
display_name: str = "Qianfan"
description: str = (
"Generate text using Baidu Qianfan chat models. Get more detail from "
"https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint."

View file

@ -1,59 +0,0 @@
from typing import Dict, Optional
from langchain_community.llms.ctransformers import CTransformers
from langflow.base.models.model import LCModelComponent
from langflow.field_typing import Text
class CTransformersComponent(LCModelComponent):
display_name = "CTransformers"
description = "Generate text using CTransformers LLM models"
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
def build_config(self):
return {
"model": {"display_name": "Model", "required": True},
"model_file": {
"display_name": "Model File",
"required": False,
"field_type": "file",
"file_types": [".bin"],
},
"model_type": {"display_name": "Model Type", "required": True},
"config": {
"display_name": "Config",
"advanced": True,
"required": False,
"field_type": "dict",
"value": '{"top_k":40,"top_p":0.95,"temperature":0.8,"repetition_penalty":1.1,"last_n_tokens":64,"seed":-1,"max_new_tokens":256,"stop":"","stream":"False","reset":"True","batch_size":8,"threads":-1,"context_length":-1,"gpu_layers":0}',
},
"input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
"system_message": {
"display_name": "System Message",
"info": "System message to pass to the model.",
},
}
def build(
self,
model: str,
model_file: str,
input_value: Text,
model_type: str,
stream: bool = False,
config: Optional[Dict] = None,
) -> Text:
output = CTransformers(
client=None,
model=model,
model_file=model_file,
model_type=model_type,
config=config, # noqa
)
return self.get_result(runnable=output, stream=stream, input_value=input_value)

View file

@ -1,148 +0,0 @@
from typing import Any, Dict, List, Optional
from langchain_community.llms.llamacpp import LlamaCpp
from langflow.base.models.model import LCModelComponent
from langflow.field_typing import Text
class LlamaCppComponent(LCModelComponent):
display_name = "LlamaCpp"
description = "Generate text using llama.cpp model."
documentation = "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
def build_config(self):
return {
"grammar": {"display_name": "Grammar", "advanced": True},
"cache": {"display_name": "Cache", "advanced": True},
"client": {"display_name": "Client", "advanced": True},
"echo": {"display_name": "Echo", "advanced": True},
"f16_kv": {"display_name": "F16 KV", "advanced": True},
"grammar_path": {"display_name": "Grammar Path", "advanced": True},
"last_n_tokens_size": {
"display_name": "Last N Tokens Size",
"advanced": True,
},
"logits_all": {"display_name": "Logits All", "advanced": True},
"logprobs": {"display_name": "Logprobs", "advanced": True},
"lora_base": {"display_name": "Lora Base", "advanced": True},
"lora_path": {"display_name": "Lora Path", "advanced": True},
"max_tokens": {"display_name": "Max Tokens", "advanced": True},
"metadata": {"display_name": "Metadata", "advanced": True},
"model_kwargs": {"display_name": "Model Kwargs", "advanced": True},
"model_path": {
"display_name": "Model Path",
"field_type": "file",
"file_types": [".bin"],
"required": True,
},
"n_batch": {"display_name": "N Batch", "advanced": True},
"n_ctx": {"display_name": "N Ctx", "advanced": True},
"n_gpu_layers": {"display_name": "N GPU Layers", "advanced": True},
"n_parts": {"display_name": "N Parts", "advanced": True},
"n_threads": {"display_name": "N Threads", "advanced": True},
"repeat_penalty": {"display_name": "Repeat Penalty", "advanced": True},
"rope_freq_base": {"display_name": "Rope Freq Base", "advanced": True},
"rope_freq_scale": {"display_name": "Rope Freq Scale", "advanced": True},
"seed": {"display_name": "Seed", "advanced": True},
"stop": {"display_name": "Stop", "advanced": True},
"streaming": {"display_name": "Streaming", "advanced": True},
"suffix": {"display_name": "Suffix", "advanced": True},
"tags": {"display_name": "Tags", "advanced": True},
"temperature": {"display_name": "Temperature"},
"top_k": {"display_name": "Top K", "advanced": True},
"top_p": {"display_name": "Top P", "advanced": True},
"use_mlock": {"display_name": "Use Mlock", "advanced": True},
"use_mmap": {"display_name": "Use Mmap", "advanced": True},
"verbose": {"display_name": "Verbose", "advanced": True},
"vocab_only": {"display_name": "Vocab Only", "advanced": True},
"input_value": {"display_name": "Input"},
"stream": {
"display_name": "Stream",
"info": "Stream the response from the model.",
},
"system_message": {
"display_name": "System Message",
"info": "System message to pass to the model.",
},
}
def build(
self,
model_path: str,
input_value: Text,
grammar: Optional[str] = None,
cache: Optional[bool] = None,
client: Optional[Any] = None,
echo: Optional[bool] = False,
f16_kv: bool = True,
grammar_path: Optional[str] = None,
last_n_tokens_size: Optional[int] = 64,
logits_all: bool = False,
logprobs: Optional[int] = None,
lora_base: Optional[str] = None,
lora_path: Optional[str] = None,
max_tokens: Optional[int] = 256,
metadata: Optional[Dict] = None,
model_kwargs: Dict = {},
n_batch: Optional[int] = 8,
n_ctx: int = 512,
n_gpu_layers: Optional[int] = 1,
n_parts: int = -1,
n_threads: Optional[int] = 1,
repeat_penalty: Optional[float] = 1.1,
rope_freq_base: float = 10000.0,
rope_freq_scale: float = 1.0,
seed: int = -1,
stop: Optional[List[str]] = [],
streaming: bool = True,
suffix: Optional[str] = "",
tags: Optional[List[str]] = [],
temperature: Optional[float] = 0.8,
top_k: Optional[int] = 40,
top_p: Optional[float] = 0.95,
use_mlock: bool = False,
use_mmap: Optional[bool] = True,
verbose: bool = True,
vocab_only: bool = False,
stream: bool = False,
) -> Text:
output = LlamaCpp(
model_path=model_path,
grammar=grammar,
cache=cache,
client=client,
echo=echo,
f16_kv=f16_kv,
grammar_path=grammar_path,
last_n_tokens_size=last_n_tokens_size,
logits_all=logits_all,
logprobs=logprobs,
lora_base=lora_base,
lora_path=lora_path,
max_tokens=max_tokens,
metadata=metadata,
model_kwargs=model_kwargs,
n_batch=n_batch,
n_ctx=n_ctx,
n_gpu_layers=n_gpu_layers,
n_parts=n_parts,
n_threads=n_threads,
repeat_penalty=repeat_penalty,
rope_freq_base=rope_freq_base,
rope_freq_scale=rope_freq_scale,
seed=seed,
stop=stop,
streaming=streaming,
suffix=suffix,
tags=tags,
temperature=temperature,
top_k=top_k,
top_p=top_p,
use_mlock=use_mlock,
use_mmap=use_mmap,
verbose=verbose,
vocab_only=vocab_only,
)
return self.get_result(runnable=output, stream=stream, input_value=input_value)

View file

@ -13,7 +13,7 @@ from langflow.field_typing import Text
class ChatOllamaComponent(LCModelComponent):
display_name = "ChatOllama"
display_name = "Ollama"
description = "Generate text using Local LLM for chat with Ollama."
icon = "Ollama"

View file

@ -7,7 +7,7 @@ from langflow.field_typing import Text
class ChatVertexAIComponent(LCModelComponent):
display_name = "ChatVertexAI"
display_name = "Vertex AI"
description = "Generate text using Vertex AI Chat large language models API."
icon = "VertexAI"

View file

@ -2,11 +2,9 @@ from .AmazonBedrockModel import AmazonBedrockComponent
from .AnthropicModel import AnthropicLLM
from .AzureOpenAIModel import AzureChatOpenAIComponent
from .BaiduQianfanChatModel import QianfanChatEndpointComponent
from .CTransformersModel import CTransformersComponent
from .CohereModel import CohereComponent
from .GoogleGenerativeAIModel import GoogleGenerativeAIComponent
from .HuggingFaceModel import HuggingFaceEndpointsComponent
from .LlamaCppModel import LlamaCppComponent
from .OllamaModel import ChatOllamaComponent
from .OpenAIModel import OpenAIModelComponent
from .VertexAiModel import ChatVertexAIComponent
@ -16,11 +14,9 @@ __all__ = [
"AnthropicLLM",
"AzureChatOpenAIComponent",
"QianfanChatEndpointComponent",
"CTransformersComponent",
"CohereComponent",
"GoogleGenerativeAIComponent",
"HuggingFaceEndpointsComponent",
"LlamaCppComponent",
"ChatOllamaComponent",
"OpenAIModelComponent",
"ChatVertexAIComponent",

View file

@ -7,7 +7,7 @@ from langflow.schema import Record
class ChatOutput(ChatComponent):
display_name = "Chat Output"
description = "Used to send a message to the chat."
description = "Used to send a chat message."
icon = "ChatOutput"
def build(
@ -17,6 +17,7 @@ class ChatOutput(ChatComponent):
input_value: Optional[str] = None,
session_id: Optional[str] = None,
return_record: Optional[bool] = False,
record_template: Optional[str] = "{text}",
) -> Union[Text, Record]:
return super().build(
sender=sender,
@ -24,4 +25,5 @@ class ChatOutput(ChatComponent):
input_value=input_value,
session_id=session_id,
return_record=return_record,
record_template=record_template,
)

View file

@ -6,11 +6,17 @@ from langflow.field_typing import Text
class TextOutput(TextComponent):
display_name = "Text Output"
description = "Used to pass text output to the next component."
description = "Used to send a text output."
field_config = {
"input_value": {"display_name": "Value"},
}
def build_config(self):
return {
"input_value": {
"display_name": "Value",
"input_types": ["Record"],
"info": "Text or Record to be passed as output.",
},
"record_template": {"display_name": "Record Template", "multiline": True},
}
def build(self, input_value: Optional[Text] = "") -> Text:
return super().build(input_value=input_value)
def build(self, input_value: Optional[Text] = "", record_template: str = "{text}") -> Text:
return super().build(input_value=input_value, record_template=record_template)

View file

@ -2,10 +2,12 @@ from .AmazonKendra import AmazonKendraRetrieverComponent
from .MetalRetriever import MetalRetrieverComponent
from .MultiQueryRetriever import MultiQueryRetrieverComponent
from .VectaraSelfQueryRetriver import VectaraSelfQueryRetriverComponent
from .VectorStoreRetriever import VectoStoreRetrieverComponent
__all__ = [
"AmazonKendraRetrieverComponent",
"MetalRetrieverComponent",
"MultiQueryRetrieverComponent",
"VectaraSelfQueryRetriverComponent",
"VectoStoreRetrieverComponent",
]

View file

@ -0,0 +1,5 @@
from .RetrieverTool import RetrieverToolComponent
from .SearchAPITool import SearchApiToolComponent
from .SearchApi import SearchApi
__all__ = ["RetrieverToolComponent", "SearchApiToolComponent", "SearchApi"]

View file

@ -23,51 +23,73 @@ class AstraDBSearchComponent(LCVectorStoreComponent):
"info": "Input value to search",
},
"embedding": {"display_name": "Embedding", "info": "Embedding to use"},
"collection_name": {"display_name": "Collection Name", "info": "Collection name"},
"token": {"display_name": "Token", "info": "Token to use", "password": True},
"api_endpoint": {"display_name": "API Endpoint", "info": "API Endpoint to use"},
"namespace": {"display_name": "Namespace", "info": "Namespace to use", "advanced": True},
"metric": {"display_name": "Metric", "info": "Metric to use", "advanced": True},
"batch_size": {"display_name": "Batch Size", "info": "Batch size to use", "advanced": True},
"collection_name": {
"display_name": "Collection Name",
"info": "The name of the collection within AstraDB where the vectors will be stored.",
},
"token": {
"display_name": "Token",
"info": "Authentication token for accessing AstraDB.",
"password": True,
},
"api_endpoint": {
"display_name": "API Endpoint",
"info": "API endpoint URL for the AstraDB service.",
},
"namespace": {
"display_name": "Namespace",
"info": "Optional namespace within AstraDB to use for the collection.",
"advanced": True,
},
"metric": {
"display_name": "Metric",
"info": "Optional distance metric for vector comparisons in the vector store.",
"advanced": True,
},
"batch_size": {
"display_name": "Batch Size",
"info": "Optional number of records to process in a single batch.",
"advanced": True,
},
"bulk_insert_batch_concurrency": {
"display_name": "Bulk Insert Batch Concurrency",
"info": "Bulk Insert Batch Concurrency to use",
"info": "Optional concurrency level for bulk insert operations.",
"advanced": True,
},
"bulk_insert_overwrite_concurrency": {
"display_name": "Bulk Insert Overwrite Concurrency",
"info": "Bulk Insert Overwrite Concurrency to use",
"info": "Optional concurrency level for bulk insert operations that overwrite existing records.",
"advanced": True,
},
"bulk_delete_concurrency": {
"display_name": "Bulk Delete Concurrency",
"info": "Bulk Delete Concurrency to use",
"info": "Optional concurrency level for bulk delete operations.",
"advanced": True,
},
"setup_mode": {
"display_name": "Setup Mode",
"info": "Setup mode for the vector store",
"info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.",
"options": ["Sync", "Async", "Off"],
"advanced": True,
},
"pre_delete_collection": {
"display_name": "Pre Delete Collection",
"info": "Pre delete collection",
"info": "Boolean flag to determine whether to delete the collection before creating a new one.",
"advanced": True,
},
"metadata_indexing_include": {
"display_name": "Metadata Indexing Include",
"info": "Metadata Indexing Include",
"info": "Optional list of metadata fields to include in the indexing.",
"advanced": True,
},
"metadata_indexing_exclude": {
"display_name": "Metadata Indexing Exclude",
"info": "Metadata Indexing Exclude",
"info": "Optional list of metadata fields to exclude from the indexing.",
"advanced": True,
},
"collection_indexing_policy": {
"display_name": "Collection Indexing Policy",
"info": "Collection Indexing Policy",
"info": "Optional dictionary defining the indexing policy for the collection.",
"advanced": True,
},
}
@ -77,9 +99,9 @@ class AstraDBSearchComponent(LCVectorStoreComponent):
embedding: Embeddings,
collection_name: str,
input_value: Text,
token: str,
api_endpoint: str,
search_type: str = "Similarity",
token: Optional[str] = None,
api_endpoint: Optional[str] = None,
namespace: Optional[str] = None,
metric: Optional[str] = None,
batch_size: Optional[int] = None,

View file

@ -0,0 +1,25 @@
from .AstraDBSearch import AstraDBSearchComponent
from .ChromaSearch import ChromaSearchComponent
from .FAISSSearch import FAISSSearchComponent
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
from .PineconeSearch import PineconeSearchComponent
from .QdrantSearch import QdrantSearchComponent
from .RedisSearch import RedisSearchComponent
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
from .VectaraSearch import VectaraSearchComponent
from .WeaviateSearch import WeaviateSearchVectorStore
from .pgvectorSearch import PGVectorSearchComponent
__all__ = [
"AstraDBSearchComponent",
"ChromaSearchComponent",
"FAISSSearchComponent",
"MongoDBAtlasSearchComponent",
"PineconeSearchComponent",
"QdrantSearchComponent",
"RedisSearchComponent",
"SupabaseSearchComponent",
"VectaraSearchComponent",
"WeaviateSearchVectorStore",
"PGVectorSearchComponent",
]

View file

@ -16,53 +16,78 @@ class AstraDBVectorStoreComponent(CustomComponent):
def build_config(self):
return {
"inputs": {"display_name": "Inputs", "info": "Inputs to AstraDB"},
"inputs": {
"display_name": "Inputs",
"info": "Optional list of records to be processed and stored in the vector store.",
},
"embedding": {"display_name": "Embedding", "info": "Embedding to use"},
"collection_name": {"display_name": "Collection Name", "info": "Collection name"},
"token": {"display_name": "Token", "info": "Token to use", "password": True},
"api_endpoint": {"display_name": "API Endpoint", "info": "API Endpoint to use"},
"namespace": {"display_name": "Namespace", "info": "Namespace to use", "advanced": True},
"metric": {"display_name": "Metric", "info": "Metric to use", "advanced": True},
"batch_size": {"display_name": "Batch Size", "info": "Batch size to use", "advanced": True},
"collection_name": {
"display_name": "Collection Name",
"info": "The name of the collection within AstraDB where the vectors will be stored.",
},
"token": {
"display_name": "Token",
"info": "Authentication token for accessing AstraDB.",
"password": True,
},
"api_endpoint": {
"display_name": "API Endpoint",
"info": "API endpoint URL for the AstraDB service.",
},
"namespace": {
"display_name": "Namespace",
"info": "Optional namespace within AstraDB to use for the collection.",
"advanced": True,
},
"metric": {
"display_name": "Metric",
"info": "Optional distance metric for vector comparisons in the vector store.",
"advanced": True,
},
"batch_size": {
"display_name": "Batch Size",
"info": "Optional number of records to process in a single batch.",
"advanced": True,
},
"bulk_insert_batch_concurrency": {
"display_name": "Bulk Insert Batch Concurrency",
"info": "Bulk Insert Batch Concurrency to use",
"info": "Optional concurrency level for bulk insert operations.",
"advanced": True,
},
"bulk_insert_overwrite_concurrency": {
"display_name": "Bulk Insert Overwrite Concurrency",
"info": "Bulk Insert Overwrite Concurrency to use",
"info": "Optional concurrency level for bulk insert operations that overwrite existing records.",
"advanced": True,
},
"bulk_delete_concurrency": {
"display_name": "Bulk Delete Concurrency",
"info": "Bulk Delete Concurrency to use",
"info": "Optional concurrency level for bulk delete operations.",
"advanced": True,
},
"setup_mode": {
"display_name": "Setup Mode",
"info": "Setup mode for the vector store",
"info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.",
"options": ["Sync", "Async", "Off"],
"advanced": True,
},
"pre_delete_collection": {
"display_name": "Pre Delete Collection",
"info": "Pre delete collection",
"info": "Boolean flag to determine whether to delete the collection before creating a new one.",
"advanced": True,
},
"metadata_indexing_include": {
"display_name": "Metadata Indexing Include",
"info": "Metadata Indexing Include",
"info": "Optional list of metadata fields to include in the indexing.",
"advanced": True,
},
"metadata_indexing_exclude": {
"display_name": "Metadata Indexing Exclude",
"info": "Metadata Indexing Exclude",
"info": "Optional list of metadata fields to exclude from the indexing.",
"advanced": True,
},
"collection_indexing_policy": {
"display_name": "Collection Indexing Policy",
"info": "Collection Indexing Policy",
"info": "Optional dictionary defining the indexing policy for the collection.",
"advanced": True,
},
}
@ -70,17 +95,17 @@ class AstraDBVectorStoreComponent(CustomComponent):
def build(
self,
embedding: Embeddings,
token: str,
api_endpoint: str,
collection_name: str,
inputs: Optional[List[Record]] = None,
token: Optional[str] = None,
api_endpoint: Optional[str] = None,
namespace: Optional[str] = None,
metric: Optional[str] = None,
batch_size: Optional[int] = None,
bulk_insert_batch_concurrency: Optional[int] = None,
bulk_insert_overwrite_concurrency: Optional[int] = None,
bulk_delete_concurrency: Optional[int] = None,
setup_mode: str = "Sync",
setup_mode: str = "Async",
pre_delete_collection: bool = False,
metadata_indexing_include: Optional[List[str]] = None,
metadata_indexing_exclude: Optional[List[str]] = None,

View file

@ -1,44 +1,26 @@
from .AstraDB import AstraDBVectorStoreComponent
from .Chroma import ChromaComponent
from .ChromaSearch import ChromaSearchComponent
from .FAISS import FAISSComponent
from .FAISSSearch import FAISSSearchComponent
from .MongoDBAtlasVector import MongoDBAtlasComponent
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
from .Pinecone import PineconeComponent
from .PineconeSearch import PineconeSearchComponent
from .Qdrant import QdrantComponent
from .QdrantSearch import QdrantSearchComponent
from .Redis import RedisComponent
from .RedisSearch import RedisSearchComponent
from .SupabaseVectorStore import SupabaseComponent
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
from .Vectara import VectaraComponent
from .VectaraSearch import VectaraSearchComponent
from .Weaviate import WeaviateVectorStoreComponent
from .WeaviateSearch import WeaviateSearchVectorStore
from .pgvector import PGVectorComponent
from .pgvectorSearch import PGVectorSearchComponent
__all__ = [
"AstraDBVectorStoreComponent",
"ChromaComponent",
"ChromaSearchComponent",
"FAISSComponent",
"FAISSSearchComponent",
"MongoDBAtlasComponent",
"MongoDBAtlasSearchComponent",
"PineconeComponent",
"PineconeSearchComponent",
"QdrantComponent",
"QdrantSearchComponent",
"RedisComponent",
"RedisSearchComponent",
"SupabaseComponent",
"SupabaseSearchComponent",
"VectaraComponent",
"VectaraSearchComponent",
"WeaviateVectorStoreComponent",
"WeaviateSearchVectorStore",
"base",
"PGVectorComponent",
"PGVectorSearchComponent",
]

View file

@ -237,10 +237,28 @@ class CodeParser:
def parse_return_statement(self, node: ast.FunctionDef) -> bool:
"""
Parses the return statement of a function or method node.
Parses the return statement of a function or method node, including nested returns.
"""
return any(isinstance(n, ast.Return) for n in node.body)
def has_return(node):
if isinstance(node, ast.Return):
return True
elif isinstance(node, ast.If):
return any(has_return(child) for child in node.body) or any(has_return(child) for child in node.orelse)
elif isinstance(node, ast.Try):
return (
any(has_return(child) for child in node.body)
or any(has_return(child) for child in node.handlers)
or any(has_return(child) for child in node.finalbody)
)
elif isinstance(node, (ast.For, ast.While)):
return any(has_return(child) for child in node.body) or any(has_return(child) for child in node.orelse)
elif isinstance(node, ast.With):
return any(has_return(child) for child in node.body)
else:
return False
return any(has_return(child) for child in node.body)
def parse_assign(self, stmt):
"""

View file

@ -32,6 +32,8 @@ def check_tools_in_params(params: Dict):
def instantiate_from_template(class_object, params: Dict):
from_template_params = {"template": params.pop("prompt", params.pop("template", ""))}
from_template_params.update(params)
if not from_template_params.get("template"):
raise ValueError("Prompt template is required")
return class_object.from_template(**from_template_params)

View file

@ -4,6 +4,7 @@ from loguru import logger
from langflow.interface.base import LangChainTypeCreator
from langflow.interface.custom_lists import llm_type_to_cls_dict
from langflow.services.deps import get_settings_service
from langflow.template.frontend_node.llms import LLMFrontendNode
from langflow.utils.util import build_template_from_class
@ -33,10 +34,11 @@ class LLMCreator(LangChainTypeCreator):
return None
def to_list(self) -> List[str]:
settings_service = get_settings_service()
return [
llm.__name__
for llm in self.type_to_loader_dict.values()
# if llm.__name__ in settings_service.settings.LLMS or settings_service.settings.DEV
if llm.__name__ in settings_service.settings.LLMS or settings_service.settings.DEV
]

View file

@ -1,7 +1,7 @@
from importlib import metadata
try:
__version__ = metadata.version(__package__)
__version__ = metadata.version("langflow")
except metadata.PackageNotFoundError:
__version__ = ""
del metadata

View file

@ -55,7 +55,7 @@
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.11",
"react-icons": "^4.10.1",
"react-icons": "^5.0.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.15.0",
@ -9363,9 +9363,9 @@
}
},
"node_modules/react-icons": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
"integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
"integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
"peerDependencies": {
"react": "*"
}

View file

@ -50,7 +50,7 @@
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.11",
"react-icons": "^4.10.1",
"react-icons": "^5.0.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.15.0",

View file

@ -211,10 +211,12 @@ export default function GenericNode({
}
}, [validationStatus, validationStatus?.params]);
// const showNode = data.showNode ?? true;
const [showNode, setShowNode] = useState(data.showNode ?? true);
useEffect(() => {
setShowNode(data.showNode ?? true);
}, [data.showNode]);
const nameEditable = true;
const emojiRegex = /\p{Emoji}/u;
@ -438,7 +440,7 @@ export default function GenericNode({
event.preventDefault();
}}
data-testid={"title-" + data.node?.display_name}
className="generic-node-tooltip-div text-primary"
className="generic-node-tooltip-div cursor-text text-primary"
>
{data.node?.display_name}
</div>

View file

@ -13,6 +13,8 @@ export default function DropdownButton({
firstButtonName,
onFirstBtnClick,
options,
plusButton = false,
dropdownOptions = true,
}: dropdownButtonPropsType): JSX.Element {
const [showOptions, setShowOptions] = useState<boolean>(false);
@ -23,28 +25,33 @@ export default function DropdownButton({
<Button
id="new-project-btn"
variant="primary"
className="relative pr-10"
className={"relative" + dropdownOptions ? "" : " pr-10"}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
onFirstBtnClick();
}}
>
{plusButton && (
<IconComponent name="Plus" className="main-page-nav-button" />
)}
{firstButtonName}
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
{dropdownOptions && (
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent

View file

@ -120,7 +120,7 @@ export default function IOView({
{/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */}
<BaseModal.Header description={CHAT_FORM_DIALOG_SUBTITLE}>
<div className="flex items-center">
<span className="pr-2">Chat</span>
<span className="pr-2">Interaction Panel</span>
<IconComponent
name="prompts"
className="h-6 w-6 pl-1 text-foreground"

View file

@ -1,5 +1,6 @@
import { useContext, useEffect } from "react";
import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa";
import { FaDiscord, FaGithub } from "react-icons/fa";
import { RiTwitterXFill } from "react-icons/ri";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import AlertDropdown from "../../alerts/alertDropDown";
import { USER_PROJECTS_HEADER } from "../../constants/constants";
@ -127,7 +128,7 @@ export default function Header(): JSX.Element {
rel="noreferrer"
className="text-muted-foreground"
>
<FaTwitter className="side-bar-button-size" />
<RiTwitterXFill className="side-bar-button-size" />
</a>
<a
href="https://discord.gg/EqksyE2EX9"

View file

@ -99,12 +99,16 @@ export default function InputComponent({
type="text"
onBlur={onInputLostFocus}
value={
selectedOption !== "" || !onChange ? selectedOption : value
(selectedOption !== "" || !onChange) && setSelectedOption
? selectedOption
: value
}
autoFocus={autoFocus}
disabled={disabled}
onClick={() => {
(selectedOption !== "" || !onChange) && setShowOptions(true);
(selectedOption !== "" || !onChange) &&
setSelectedOption &&
setShowOptions(true);
}}
required={required}
className={classNames(
@ -115,8 +119,12 @@ export default function InputComponent({
? " text-clip password "
: "",
editNode ? " input-edit-node " : "",
password && selectedOption === "" && editNode ? "pr-8" : "",
password && selectedOption === "" && !editNode ? "pr-10" : "",
password && setSelectedOption ? "pr-16" : "",
(!password && setSelectedOption) ||
(password && !setSelectedOption)
? "pr-8"
: "",
className!
)}
placeholder={password && editNode ? "Key" : placeholder}
@ -179,18 +187,32 @@ export default function InputComponent({
setShowOptions(false);
}}
>
<div className="flex w-full items-center justify-between">
<div className="group flex w-full items-center justify-between">
<div className="flex items-center">
<ForwardedIconComponent
name="Check"
<div
className={cn(
"mr-2 h-4 w-4 text-primary",
"relative mr-2 h-4 w-4",
selectedOption === option
? "opacity-100"
: "opacity-0"
)}
aria-hidden="true"
/>
>
<div className="absolute opacity-100 transition-all group-hover:opacity-0">
<ForwardedIconComponent
name="Check"
className="mr-2 h-4 w-4 text-primary"
aria-hidden="true"
/>
</div>
<div className="absolute opacity-0 transition-all group-hover:opacity-100">
<ForwardedIconComponent
name="X"
className="mr-2 h-4 w-4 text-status-red"
aria-hidden="true"
/>
</div>
</div>
{option}
</div>
{optionButton && optionButton(option)}
@ -206,10 +228,12 @@ export default function InputComponent({
<div
className={cn(
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
selectedOption !== "" || !onChange ? "" : "hidden"
(selectedOption !== "" || !onChange) && setSelectedOption
? ""
: "hidden"
)}
onClick={
selectedOption !== "" || !onChange
(selectedOption !== "" || !onChange) && setSelectedOption
? (e) => {
setShowOptions((old) => !old);
e.preventDefault();
@ -221,30 +245,32 @@ export default function InputComponent({
</>
)}
<span
className={cn(
password && selectedOption === "" ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2.5"
)}
>
<button
onClick={() => {
setShowOptions(!showOptions);
}}
{setSelectedOption && (
<span
className={cn(
selectedOption !== ""
? "text-medium-indigo"
: "text-muted-foreground",
"hover:text-accent-foreground"
password && selectedOption === "" ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2.5"
)}
>
<ForwardedIconComponent
name={optionsIcon}
className={"h-4 w-4"}
aria-hidden="true"
/>
</button>
</span>
<button
onClick={() => {
setShowOptions(!showOptions);
}}
className={cn(
selectedOption !== ""
? "text-medium-indigo"
: "text-muted-foreground",
"hover:text-accent-foreground"
)}
>
<ForwardedIconComponent
name={optionsIcon}
className={"h-4 w-4"}
aria-hidden="true"
/>
</button>
</span>
)}
{password && selectedOption === "" && (
<button

View file

@ -107,7 +107,7 @@ export const CODE_DIALOG_SUBTITLE =
* @constant
*/
export const CHAT_FORM_DIALOG_SUBTITLE =
"Set up the input variables defined in prompt templates. Interact with agents and chains.";
"Adjust or overwrite input values, inspect outputs or interact through the chat console.";
/**
* The base text for subtitle of Edit Node Dialog
@ -732,12 +732,12 @@ export const PRIORITY_SIDEBAR_ORDER = [
"saved_components",
"inputs",
"outputs",
"prompts",
"data",
"prompt",
"models",
"vectorstores",
"vectorsearch",
"helpers",
"experimental",
];
export const NATIVE_CATEGORIES = [
"inputs",

View file

@ -33,7 +33,7 @@ export default function ChatInput({
<div className="relative">
<Textarea
onKeyDown={(event) => {
if (event.key === "Enter" && !lockChat && !event.shiftKey) {
if (event.key === "Enter" && !event.nativeEvent.isComposing && !lockChat && !event.shiftKey) {
sendMessage();
}
}}

View file

@ -5,7 +5,6 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";
import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal";
import useAlertStore from "../../../../stores/alertStore";
@ -282,35 +281,7 @@ export default function ExtraSidebar(): JSX.Element {
</div>
</div>
)}
{index === PRIORITY_SIDEBAR_ORDER.length - 1 && (
<>
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<SparklesIcon
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<LinkIcon className="h-4 w-4 text-foreground" />
</div>
</div>
</a>
<div className="p-2 px-4 text-sm font-semibold" key={index}>
Legacy Components
</div>
</>
)}
<DisclosureComponent
openDisc={
getFilterEdge.length !== 0 || search.length !== 0
@ -371,6 +342,32 @@ export default function ExtraSidebar(): JSX.Element {
))}
</div>
</DisclosureComponent>
{index === Object.keys(dataFilter).length - 1 && (
<>
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<SparklesIcon
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<LinkIcon className="h-4 w-4 text-foreground" />
</div>
</div>
</a>
</>
)}
</>
) : (
<div key={index}></div>

View file

@ -85,7 +85,9 @@ export default function NodeToolbarComponent({
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false);
const [showOverrideModal, setShowOverrideModal] = useState(false);
const [flowComponent, setFlowComponent] = useState<FlowType>();
const [flowComponent, setFlowComponent] = useState<FlowType>(
createFlowComponent(cloneDeep(data), version)
);
const openInNewTab = (url) => {
window.open(url, "_blank", "noreferrer");
@ -285,7 +287,13 @@ export default function NodeToolbarComponent({
event.preventDefault();
setShowconfirmShare((state) => !state);
}
if (selected && (event.ctrlKey || event.metaKey) && event.key === "q") {
if (
selected &&
(event.ctrlKey || event.metaKey) &&
event.shiftKey &&
event.key === "Q"
) {
event.preventDefault();
if (isMinimal) {
setShowState((show) => !show);
@ -341,6 +349,10 @@ export default function NodeToolbarComponent({
title: `${data.id} docs is not available at the moment.`,
});
}
if (selected && (event.ctrlKey || event.metaKey) && event.key === "j") {
event.preventDefault();
downloadNode(flowComponent!);
}
}
document.addEventListener("keydown", onKeyDown);
@ -499,15 +511,17 @@ export default function NodeToolbarComponent({
/>
</SelectItem>
)}
{!hasStore && (
{(!hasStore || !hasApiKey || !validApiKey) && (
<SelectItem value={"Download"}>
<div className="flex">
<IconComponent
name="Download"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Download{" "}
</div>{" "}
<ToolbarSelectItem
value="Download"
shift={false}
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
icon="Download"
styleObj={{ iconClasses: "relative top-0.5 mr-2 h-4 w-4" }}
keyboardKey={"J"}
dataTestId={"Dowload-button-nodeToolbar"}
/>
</SelectItem>
)}
<SelectItem
@ -525,24 +539,14 @@ export default function NodeToolbarComponent({
</SelectItem>
{isMinimal && (
<SelectItem value={"show"}>
<div className="flex">
<IconComponent
name={showNode ? "Minimize2" : "Maximize2"}
className="relative top-0.5 mr-2 h-4 w-4"
/>
{showNode ? "Minimize" : "Expand"}
{navigator.userAgent.toUpperCase().includes("MAC") ? (
<IconComponent
name="Command"
className="absolute right-[1.25rem] top-[0.65em] h-3.5 w-3.5 stroke-2"
></IconComponent>
) : (
<span className="absolute right-[1.30rem] top-[0.40em] stroke-2">
Ctrl +{" "}
</span>
)}
<span className="absolute right-2 top-[0.46em]">Q</span>
</div>
<ToolbarSelectItem
icon={showNode ? "Minimize2" : "Maximize2"}
value={showNode ? "Minimize" : "Expand"}
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
shift={true}
keyboardKey={"Q"}
dataTestId={"minimize-button-nodeToolbar"}
/>
</SelectItem>
)}
{isGroup && (

View file

@ -105,6 +105,8 @@ export default function HomePage(): JSX.Element {
firstButtonName="New Project"
onFirstBtnClick={() => setOpenModal(true)}
options={dropdownOptions}
plusButton={true}
dropdownOptions={false}
/>
</div>
}

View file

@ -4,7 +4,7 @@ import { DarkStoreType } from "../types/zustand/dark";
const startedStars = Number(window.localStorage.getItem("githubStars")) ?? 0;
export const useDarkStore = create<DarkStoreType>((set) => ({
export const useDarkStore = create<DarkStoreType>((set, get) => ({
dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false,
stars: startedStars,
version: "",
@ -15,14 +15,23 @@ export const useDarkStore = create<DarkStoreType>((set) => ({
});
},
refreshStars: () => {
if (window.localStorage.getItem("githubStars") !== null) {
set(() => ({ stars: startedStars }));
return;
let lastUpdated = window.localStorage.getItem("githubStarsLastUpdated");
let diff = 0;
// check if lastUpdated actually exists
if (lastUpdated !== null) {
diff = Math.abs(new Date().getTime() - new Date(lastUpdated).getTime());
}
getRepoStars("logspace-ai", "langflow").then((res) => {
window.localStorage.setItem("githubStars", res.toString());
set(() => ({ stars: res }));
});
// if lastUpdated is null or the difference is greater than 2 hours
if (lastUpdated === null || diff > 7200000) {
getRepoStars("logspace-ai", "langflow").then((res) => {
window.localStorage.setItem("githubStars", res.toString());
window.localStorage.setItem(
"githubStarsLastUpdated",
new Date().toString()
);
set(() => ({ stars: res, lastUpdated: new Date() }));
});
}
},
}));

View file

@ -689,6 +689,8 @@ export type dropdownButtonPropsType = {
firstButtonName: string;
onFirstBtnClick: () => void;
options: Array<{ name: string; onBtnClick: () => void }>;
plusButton?: boolean;
dropdownOptions?: boolean;
};
export type IOInputProps = {

View file

@ -119,6 +119,8 @@ export async function buildVertices({
try {
onValidateNodes(verticesOrderResponse.verticesToRun);
} catch (e) {
useFlowStore.getState().setIsBuilding(false);
return;
}
}

View file

@ -1170,7 +1170,7 @@ export function downloadNode(NodeFLow: FlowType) {
type: "application/json",
});
element.href = URL.createObjectURL(file);
element.download = `${NodeFLow.name}.json`;
element.download = `${NodeFLow?.name??"node"}.json`;
element.click();
}

View file

@ -236,6 +236,7 @@ export const nodeColors: { [char: string]: string } = {
embeddings: "#42BAA7",
documentloaders: "#7AAE42",
vectorstores: "#AA8742",
vectorsearch: "#AA8742",
textsplitters: "#B47CB5",
toolkits: "#DB2C2C",
wrappers: "#E6277A",
@ -248,6 +249,7 @@ export const nodeColors: { [char: string]: string } = {
retrievers: "#e6b25a",
unknown: "#9CA3AF",
custom_components: "#ab11ab",
Record:"#31a3cc"
};
export const nodeNames: { [char: string]: string } = {
@ -267,6 +269,7 @@ export const nodeNames: { [char: string]: string } = {
embeddings: "Embeddings",
documentloaders: "Loaders",
vectorstores: "Vector Stores",
vectorsearch: "Vector Search",
toolkits: "Toolkits",
wrappers: "Wrappers",
textsplitters: "Text Splitters",
@ -369,6 +372,7 @@ export const nodeIconsLucide: iconsType = {
saved_components: GradientSave,
documentloaders: Paperclip,
vectorstores: Layers,
vectorsearch: Search,
toolkits: Package2,
textsplitters: Scissors,
wrappers: Gift,
@ -387,7 +391,6 @@ export const nodeIconsLucide: iconsType = {
Trash2,
Boxes,
Network,
X,
XCircle,
Info,
CheckCircle2,

View file

@ -0,0 +1,358 @@
{
"id": "3f5e860d-1676-4fba-9d86-06db9f51e43a",
"data": {
"nodes": [
{
"id": "ChatInput-7hPDT",
"type": "genericNode",
"position": {
"x": 687,
"y": 41.9375
},
"data": {
"type": "ChatInput",
"node": {
"template": {
"code": {
"type": "code",
"required": true,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Used to get user input from the chat.\"\n icon = \"ChatInput\"\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "code",
"advanced": true,
"dynamic": true,
"info": "",
"load_from_db": false,
"title_case": false
},
"input_value": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "input_value",
"display_name": "Message",
"advanced": false,
"input_types": ["Text"],
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false
},
"return_record": {
"type": "bool",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "return_record",
"display_name": "Return Record",
"advanced": false,
"dynamic": false,
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
"load_from_db": false,
"title_case": false
},
"sender": {
"type": "str",
"required": false,
"placeholder": "",
"list": true,
"show": true,
"multiline": false,
"value": "User",
"fileTypes": [],
"file_path": "",
"password": false,
"options": ["Machine", "User"],
"name": "sender",
"display_name": "Sender Type",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"sender_name": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": "User",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "sender_name",
"display_name": "Sender Name",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"session_id": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "session_id",
"display_name": "Session ID",
"advanced": true,
"dynamic": false,
"info": "If provided, the message will be stored in the memory.",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"_type": "CustomComponent"
},
"description": "Used to get user input from the chat.",
"icon": "ChatInput",
"base_classes": ["Text", "object", "str", "Record"],
"display_name": "Chat Input",
"documentation": "",
"custom_fields": {
"sender": null,
"sender_name": null,
"input_value": null,
"session_id": null,
"return_record": null
},
"output_types": ["Text", "Record"],
"field_formatters": {},
"frozen": false,
"field_order": [],
"beta": false
},
"id": "ChatInput-7hPDT"
},
"selected": false,
"width": 384,
"height": 569,
"positionAbsolute": {
"x": 687,
"y": 41.9375
},
"dragging": false
},
{
"id": "ChatOutput-4sm9Z",
"type": "genericNode",
"position": {
"x": 4.939451516507972,
"y": 103.3695231904992
},
"data": {
"type": "ChatOutput",
"node": {
"template": {
"code": {
"type": "code",
"required": true,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Used to send a message to the chat.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "code",
"advanced": true,
"dynamic": true,
"info": "",
"load_from_db": false,
"title_case": false
},
"input_value": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "input_value",
"display_name": "Message",
"advanced": false,
"input_types": ["Text"],
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false
},
"return_record": {
"type": "bool",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "return_record",
"display_name": "Return Record",
"advanced": false,
"dynamic": false,
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
"load_from_db": false,
"title_case": false
},
"sender": {
"type": "str",
"required": false,
"placeholder": "",
"list": true,
"show": true,
"multiline": false,
"value": "Machine",
"fileTypes": [],
"file_path": "",
"password": false,
"options": ["Machine", "User"],
"name": "sender",
"display_name": "Sender Type",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"sender_name": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": "AI",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "sender_name",
"display_name": "Sender Name",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"session_id": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "session_id",
"display_name": "Session ID",
"advanced": true,
"dynamic": false,
"info": "If provided, the message will be stored in the memory.",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"_type": "CustomComponent"
},
"description": "Used to send a message to the chat.",
"icon": "ChatOutput",
"base_classes": ["Text", "object", "str", "Record"],
"display_name": "Chat Output",
"documentation": "",
"custom_fields": {
"sender": null,
"sender_name": null,
"input_value": null,
"session_id": null,
"return_record": null
},
"output_types": ["Text", "Record"],
"field_formatters": {},
"frozen": false,
"field_order": [],
"beta": false
},
"id": "ChatOutput-4sm9Z"
},
"selected": true,
"width": 384,
"height": 569,
"positionAbsolute": {
"x": 4.939451516507972,
"y": 103.3695231904992
},
"dragging": false
}
],
"edges": [
{
"source": "ChatOutput-4sm9Z",
"sourceHandle": "{œbaseClassesœ:[œTextœ,œobjectœ,œstrœ,œRecordœ],œdataTypeœ:œChatOutputœ,œidœ:œChatOutput-4sm9Zœ}",
"target": "ChatInput-7hPDT",
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatInput-7hPDTœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
"data": {
"targetHandle": {
"fieldName": "input_value",
"id": "ChatInput-7hPDT",
"inputTypes": ["Text"],
"type": "str"
},
"sourceHandle": {
"baseClasses": ["Text", "object", "str", "Record"],
"dataType": "ChatOutput",
"id": "ChatOutput-4sm9Z"
}
},
"style": {
"stroke": "#555"
},
"className": "stroke-gray-900 stroke-connection",
"id": "reactflow__edge-ChatOutput-4sm9Z{œbaseClassesœ:[œTextœ,œobjectœ,œstrœ,œRecordœ],œdataTypeœ:œChatOutputœ,œidœ:œChatOutput-4sm9Zœ}-ChatInput-7hPDT{œfieldNameœ:œinput_valueœ,œidœ:œChatInput-7hPDTœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
}
],
"viewport": {
"x": 316.78239920440234,
"y": 182.84104875768622,
"zoom": 0.6070974421975234
}
},
"description": "flow to test ChatInput and output",
"name": "ChatITest",
"last_tested_version": "1.0.0a0",
"is_component": false
}

View file

@ -0,0 +1,46 @@
import { test } from "@playwright/test";
import { readFileSync } from "fs";
test.beforeEach(async ({ page }) => {
// await page.waitForTimeout(20000);
// test.setTimeout(120000);
});
test("chat_io_teste", async ({ page }) => {
await page.goto("/");
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
// Read your file into a buffer.
const jsonContent = readFileSync(
"tests/end-to-end/assets/ChatTest.json",
"utf-8"
);
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data], "ChatTest.json", {
type: "application/json",
});
dt.items.add(file);
return dt;
}, jsonContent);
await page.locator('//*[@id="new-project-btn"]').click();
await page.waitForTimeout(2000);
await page.getByTestId("blank-flow").click();
await page.waitForTimeout(2000);
// Now dispatch
await page.dispatchEvent(
'//*[@id="react-flow-id"]/div[1]/div[1]/div',
"drop",
{
dataTransfer,
}
);
await page.getByLabel("fit view").click();
await page.getByText("Run", { exact: true }).click();
await page.getByPlaceholder("Send a message...").click();
await page.getByPlaceholder("Send a message...").fill("teste");
await page.getByRole("button").nth(1).click();
});

View file

@ -25,86 +25,31 @@ test("CodeAreaModalComponent", async ({ page }) => {
await page.getByTestId("code-button-modal").click();
let value = await page.locator('//*[@id="codeValue"]').inputValue();
if (
value !=
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text'
) {
expect(false).toBeTruthy();
}
const code =
'def python_function(text: str) -> str:\n """This is a default python function that returns the input text"""\n return text';
const wCode =
'def python_function(text: str) -> st: """This is a default python function that returns the input text""" return text';
const assertCode =
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text';
await page
.locator("#CodeEditor div")
.filter({ hasText: "def python_function(text: str" })
.nth(1)
.click();
await page.locator("textarea").press("Control+a");
await page.locator("textarea").fill(wCode);
await page.locator('//*[@id="checkAndSaveBtn"]').click();
await page.getByTestId("div-generic-node").click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showdescription"]').click();
await page.waitForTimeout(1000);
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
await page.getByText("invalid syntax (<unknown>, line 1)").isVisible()
).toBeTruthy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="saveChangesBtn"]').click();
const plusButtonLocator = page.locator('//*[@id="code-input-0"]');
const elementCount = await plusButtonLocator.count();
if (elementCount === 0) {
expect(true).toBeTruthy();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="saveChangesBtn"]').click();
await page.getByTestId("div-generic-node").click();
await page.getByTestId("code-button-modal").click();
}
await page.locator("textarea").press("Control+a");
await page.locator("textarea").fill(wCode);
await page.locator("textarea").fill(code);
await page.locator('//*[@id="checkAndSaveBtn"]').click();
expect(await page.getByText("Code is ready to run").isVisible()).toBeTruthy();
await page.getByTestId("code-button-modal").click();
expect(await page.locator('//*[@id="codeValue"]').inputValue()).toBe(
assertCode
);
});

View file

@ -0,0 +1,17 @@
import { expect, test } from "@playwright/test";
test("curl_api_generation", async ({ page, context }) => {
await page.goto("/");
await page.locator('//*[@id="new-project-btn"]').click();
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByRole("heading", { name: "Data Ingestion" }).click();
await page.waitForTimeout(2000);
await page.getByText("API", { exact: true }).click();
await page.getByRole("tab", { name: "cURL" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent = await handle.jsonValue();
expect(clipboardContent.length).toBeGreaterThan(0);
});

View file

@ -0,0 +1,17 @@
import { expect, test } from "@playwright/test";
test("python_api_generation", async ({ page, context }) => {
await page.goto("/");
await page.locator('//*[@id="new-project-btn"]').click();
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByRole("heading", { name: "Data Ingestion" }).click();
await page.waitForTimeout(2000);
await page.getByText("API", { exact: true }).click();
await page.getByRole("tab", { name: "Python API" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent = await handle.jsonValue();
expect(clipboardContent.length).toBeGreaterThan(0);
});

View file

@ -0,0 +1,38 @@
import { expect, test } from "@playwright/test";
test("curl_api_generation", async ({ page, context }) => {
await page.goto("/");
await page.locator('//*[@id="new-project-btn"]').click();
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByRole("heading", { name: "Data Ingestion" }).click();
await page.waitForTimeout(2000);
await page.getByText("API", { exact: true }).click();
await page.getByRole("tab", { name: "cURL" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent = await handle.jsonValue();
const oldValue = clipboardContent;
expect(clipboardContent.length).toBeGreaterThan(0);
await page.getByRole("tab", { name: "Tweaks" }).click();
await page
.getByRole("heading", { name: "URL" })
.locator("div")
.first()
.click();
await page.getByRole("textbox", { name: "Type something..." }).click();
await page
.getByRole("textbox", { name: "Type something..." })
.press("Control+a");
await page.getByRole("textbox", { name: "Type something..." }).fill("teste");
await page.getByRole("tab", { name: "cURL" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle2 = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent2 = await handle2.jsonValue();
const newValue = clipboardContent2;
expect(oldValue).not.toBe(newValue);
expect(clipboardContent2.length).toBeGreaterThan(clipboardContent.length);
});