Adds an Info button that links to docs (#539)

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-06-26 23:16:58 +00:00 • committed by GitHub
commit e362cfcf16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 393 additions and 270 deletions

View file

@ -1,141 +1,247 @@
---
agents: agents:
- ZeroShotAgent ZeroShotAgent:
- JsonAgent documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
- CSVAgent JsonAgent:
- AgentInitializer documentation: "https://python.langchain.com/docs/modules/agents/toolkits/openapi"
- VectorStoreAgent CSVAgent:
- VectorStoreRouterAgent documentation: "https://python.langchain.com/docs/modules/agents/toolkits/csv"
- SQLAgent AgentInitializer:
documentation: "https://python.langchain.com/docs/modules/agents/agent_types/"
VectorStoreAgent:
documentation: ""
VectorStoreRouterAgent:
documentation: ""
SQLAgent:
documentation: ""
chains: chains:
- LLMChain LLMChain:
- LLMMathChain documentation: "https://python.langchain.com/docs/modules/chains/foundational/llm_chain"
- LLMCheckerChain LLMMathChain:
- ConversationChain documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_math"
- SeriesCharacterChain LLMCheckerChain:
- MidJourneyPromptChain documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_checker"
- TimeTravelGuideChain ConversationChain:
- SQLDatabaseChain documentation: ""
- RetrievalQA SeriesCharacterChain:
- RetrievalQAWithSourcesChain documentation: ""
- ConversationalRetrievalChain MidJourneyPromptChain:
- CombineDocsChain documentation: ""
TimeTravelGuideChain:
documentation: ""
SQLDatabaseChain:
documentation: ""
RetrievalQA:
documentation: "https://python.langchain.com/docs/modules/chains/popular/vector_db_qa"
RetrievalQAWithSourcesChain:
documentation: ""
ConversationalRetrievalChain:
documentation: "https://python.langchain.com/docs/modules/chains/popular/chat_vector_db"
CombineDocsChain:
documentation: ""
documentloaders: documentloaders:
- AirbyteJSONLoader AirbyteJSONLoader:
- CoNLLULoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/airbyte_json"
- CSVLoader CoNLLULoader:
- UnstructuredEmailLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/conll-u"
- EverNoteLoader CSVLoader:
- FacebookChatLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/csv"
- GutenbergLoader UnstructuredEmailLoader:
- BSHTMLLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/email"
- UnstructuredHTMLLoader EverNoteLoader:
# - UnstructuredImageLoader # Issue with Python 3.11 (https://github.com/Unstructured-IO/unstructured-inference/issues/83) documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/evernote"
- UnstructuredMarkdownLoader FacebookChatLoader:
- PyPDFLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/facebook_chat"
- UnstructuredPowerPointLoader GutenbergLoader:
- SRTLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gutenberg"
- TelegramChatLoader BSHTMLLoader:
- TextLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
- UnstructuredWordDocumentLoader UnstructuredHTMLLoader:
- WebBaseLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
- AZLyricsLoader UnstructuredMarkdownLoader:
- CollegeConfidentialLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/markdown"
- HNLoader PyPDFLoader:
- IFixitLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/pdf"
- IMSDbLoader UnstructuredPowerPointLoader:
- GitbookLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_powerpoint"
- ReadTheDocsLoader SRTLoader:
- SlackDirectoryLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/subtitle"
- NotionDirectoryLoader TelegramChatLoader:
- DirectoryLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/telegram"
- GitLoader TextLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/"
UnstructuredWordDocumentLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_word"
WebBaseLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/web_base"
AZLyricsLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/azlyrics"
CollegeConfidentialLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/college_confidential"
HNLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/hacker_news"
IFixitLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/ifixit"
IMSDbLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/imsdb"
GitbookLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gitbook"
ReadTheDocsLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/readthedocs_documentation"
SlackDirectoryLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/slack"
NotionDirectoryLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/notion"
DirectoryLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/file_directory"
GitLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/git"
embeddings: embeddings:
- OpenAIEmbeddings OpenAIEmbeddings:
- HuggingFaceEmbeddings documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai"
- CohereEmbeddings HuggingFaceEmbeddings:
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers"
CohereEmbeddings:
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/cohere"
llms: llms:
- OpenAI OpenAI:
# - AzureOpenAI documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/openai"
# - AzureChatOpenAI ChatOpenAI:
- ChatOpenAI documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai"
- LlamaCpp LlamaCpp:
- CTransformers documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
- Cohere CTransformers:
- Anthropic documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
- ChatAnthropic Cohere:
- HuggingFaceHub documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
Anthropic:
documentation: ""
ChatAnthropic:
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
HuggingFaceHub:
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/huggingface_hub"
memories: memories:
- ConversationBufferMemory ConversationBufferMemory:
- ConversationSummaryMemory documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
- ConversationKGMemory ConversationSummaryMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
ConversationKGMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/kg"
ConversationBufferWindowMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/buffer_window"
VectorStoreRetrieverMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/vectorstore_retriever_memory"
prompts: prompts:
- PromptTemplate PromptTemplate:
- FewShotPromptTemplate documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
- ZeroShotPrompt ZeroShotPrompt:
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
textsplitters: textsplitters:
- CharacterTextSplitter CharacterTextSplitter:
- RecursiveCharacterTextSplitter documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
# - LatexTextSplitter RecursiveCharacterTextSplitter:
# - PythonCodeTextSplitter documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter"
toolkits: toolkits:
- OpenAPIToolkit OpenAPIToolkit:
- JsonToolkit documentation: ""
- VectorStoreInfo JsonToolkit:
- VectorStoreRouterToolkit documentation: ""
- VectorStoreToolkit VectorStoreInfo:
documentation: ""
VectorStoreRouterToolkit:
documentation: ""
VectorStoreToolkit:
documentation: ""
tools: tools:
- Search Search:
- PAL-MATH documentation: ""
- Calculator PAL-MATH:
- Serper Search documentation: ""
- Tool Calculator:
- PythonFunctionTool documentation: ""
- PythonFunction Serper Search:
- JsonSpec documentation: ""
- News API Tool:
- TMDB API documentation: ""
- Podcast API PythonFunctionTool:
- QuerySQLDataBaseTool documentation: ""
- InfoSQLDatabaseTool PythonFunction:
- ListSQLDatabaseTool documentation: ""
# - QueryCheckerTool JsonSpec:
- BingSearchRun documentation: ""
- GoogleSearchRun News API:
- GoogleSearchResults documentation: ""
- GoogleSerperRun TMDB API:
- JsonListKeysTool documentation: ""
- JsonGetValueTool Podcast API:
- PythonREPLTool documentation: ""
- PythonAstREPLTool QuerySQLDataBaseTool:
- RequestsGetTool documentation: ""
- RequestsPostTool InfoSQLDatabaseTool:
- RequestsPatchTool documentation: ""
- RequestsPutTool ListSQLDatabaseTool:
- RequestsDeleteTool documentation: ""
- WikipediaQueryRun BingSearchRun:
- WolframAlphaQueryRun documentation: ""
GoogleSearchRun:
documentation: ""
GoogleSearchResults:
documentation: ""
GoogleSerperRun:
documentation: ""
JsonListKeysTool:
documentation: ""
JsonGetValueTool:
documentation: ""
PythonREPLTool:
documentation: ""
PythonAstREPLTool:
documentation: ""
RequestsGetTool:
documentation: ""
RequestsPostTool:
documentation: ""
RequestsPatchTool:
documentation: ""
RequestsPutTool:
documentation: ""
RequestsDeleteTool:
documentation: ""
WikipediaQueryRun:
documentation: ""
WolframAlphaQueryRun:
documentation: ""
utilities: utilities:
- BingSearchAPIWrapper BingSearchAPIWrapper:
- GoogleSearchAPIWrapper documentation: ""
- GoogleSerperAPIWrapper GoogleSearchAPIWrapper:
- SearxResults documentation: ""
- SearxSearchWrapper GoogleSerperAPIWrapper:
- SerpAPIWrapper documentation: ""
- WikipediaAPIWrapper SearxResults:
- WolframAlphaAPIWrapper documentation: ""
# - ZapierNLAWrapper SearxSearchWrapper:
- SQLDatabase documentation: ""
SerpAPIWrapper:
documentation: ""
WikipediaAPIWrapper:
documentation: ""
WolframAlphaAPIWrapper:
documentation: ""
vectorstores: vectorstores:
- Chroma Chroma:
- Qdrant documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
- Weaviate Qdrant:
- FAISS documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
- Pinecone Weaviate:
- SupabaseVectorStore documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/weaviate"
- MongoDBAtlasVectorSearch FAISS:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
Pinecone:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone"
SupabaseVectorStore:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/supabase"
MongoDBAtlasVectorSearch:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/mongodb_atlas_vector_search"
wrappers: wrappers:
- RequestsWrapper RequestsWrapper:
# - ChatPromptTemplate documentation: ""
# - SystemMessagePromptTemplate
# - HumanMessagePromptTemplate

View file

@ -8,6 +8,7 @@ from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.template.base import Template from langflow.template.template.base import Template
from langflow.utils.logger import logger from langflow.utils.logger import logger
from langflow.settings import settings
# Assuming necessary imports for Field, Template, and FrontendNode classes # Assuming necessary imports for Field, Template, and FrontendNode classes
@ -15,12 +16,29 @@ from langflow.utils.logger import logger
class LangChainTypeCreator(BaseModel, ABC): class LangChainTypeCreator(BaseModel, ABC):
type_name: str type_name: str
type_dict: Optional[Dict] = None type_dict: Optional[Dict] = None
name_docs_dict: Optional[Dict[str, str]] = None
@property @property
def frontend_node_class(self) -> Type[FrontendNode]: def frontend_node_class(self) -> Type[FrontendNode]:
"""The class type of the FrontendNode created in frontend_node.""" """The class type of the FrontendNode created in frontend_node."""
return FrontendNode return FrontendNode
@property
def docs_map(self) -> Dict[str, str]:
"""A dict with the name of the component as key and the documentation link as value."""
if self.name_docs_dict is None:
try:
type_settings = getattr(settings, self.type_name)
self.name_docs_dict = {
name: value_dict["documentation"]
for name, value_dict in type_settings.items()
}
except AttributeError as exc:
logger.error(exc)
self.name_docs_dict = {}
return self.name_docs_dict
@property @property
@abstractmethod @abstractmethod
def type_to_loader_dict(self) -> Dict: def type_to_loader_dict(self) -> Dict:
@ -83,7 +101,7 @@ class LangChainTypeCreator(BaseModel, ABC):
signature.add_extra_fields() signature.add_extra_fields()
signature.add_extra_base_classes() signature.add_extra_base_classes()
signature.set_documentation(self.docs_map.get(name, ""))
return signature return signature

View file

@ -1,5 +1,5 @@
import json import json
from typing import Any, Callable, Dict, Sequence from typing import Any, Callable, Dict, Sequence, Type
from langchain.agents import ZeroShotAgent from langchain.agents import ZeroShotAgent
from langchain.agents import agent as agent_module from langchain.agents import agent as agent_module
@ -16,6 +16,10 @@ from langflow.interface.toolkits.base import toolkits_creator
from langflow.interface.chains.base import chain_creator from langflow.interface.chains.base import chain_creator
from langflow.interface.utils import load_file_into_dict from langflow.interface.utils import load_file_into_dict
from langflow.utils import validate from langflow.utils import validate
from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore
from langchain.document_loaders.base import BaseLoader
from langchain.prompts.base import BasePromptTemplate
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any: def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
@ -76,7 +80,7 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
return class_object(**params) return class_object(**params)
def instantiate_chains(node_type, class_object, params): def instantiate_chains(node_type, class_object: Type[Chain], params: Dict):
if "retriever" in params and hasattr(params["retriever"], "as_retriever"): if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
params["retriever"] = params["retriever"].as_retriever() params["retriever"] = params["retriever"].as_retriever()
if node_type in chain_creator.from_method_nodes: if node_type in chain_creator.from_method_nodes:
@ -88,11 +92,11 @@ def instantiate_chains(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_agent(class_object, params): def instantiate_agent(class_object: Type[agent_module.Agent], params: Dict):
return load_agent_executor(class_object, params) return load_agent_executor(class_object, params)
def instantiate_prompt(node_type, class_object, params): def instantiate_prompt(node_type, class_object: Type[BasePromptTemplate], params: Dict):
if node_type == "ZeroShotPrompt": if node_type == "ZeroShotPrompt":
if "tools" not in params: if "tools" not in params:
params["tools"] = [] params["tools"] = []
@ -100,7 +104,7 @@ def instantiate_prompt(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_tool(node_type, class_object, params): def instantiate_tool(node_type, class_object: Type[BaseTool], params: Dict):
if node_type == "JsonSpec": if node_type == "JsonSpec":
params["dict_"] = load_file_into_dict(params.pop("path")) params["dict_"] = load_file_into_dict(params.pop("path"))
return class_object(**params) return class_object(**params)
@ -118,7 +122,7 @@ def instantiate_tool(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_toolkit(node_type, class_object, params): def instantiate_toolkit(node_type, class_object: Type[BaseToolkit], params: Dict):
loaded_toolkit = class_object(**params) loaded_toolkit = class_object(**params)
# Commenting this out for now to use toolkits as normal tools # Commenting this out for now to use toolkits as normal tools
# if toolkits_creator.has_create_function(node_type): # if toolkits_creator.has_create_function(node_type):
@ -128,7 +132,7 @@ def instantiate_toolkit(node_type, class_object, params):
return loaded_toolkit return loaded_toolkit
def instantiate_embedding(class_object, params): def instantiate_embedding(class_object, params: Dict):
params.pop("model", None) params.pop("model", None)
params.pop("headers", None) params.pop("headers", None)
try: try:
@ -142,7 +146,7 @@ def instantiate_embedding(class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_vectorstore(class_object, params): def instantiate_vectorstore(class_object: Type[VectorStore], params: Dict):
search_kwargs = params.pop("search_kwargs", {}) search_kwargs = params.pop("search_kwargs", {})
if initializer := vecstore_initializer.get(class_object.__name__): if initializer := vecstore_initializer.get(class_object.__name__):
vecstore = initializer(class_object, params) vecstore = initializer(class_object, params)
@ -158,7 +162,7 @@ def instantiate_vectorstore(class_object, params):
return vecstore return vecstore
def instantiate_documentloader(class_object, params): def instantiate_documentloader(class_object: Type[BaseLoader], params: Dict):
if "file_filter" in params: if "file_filter" in params:
# file_filter will be a string but we need a function # file_filter will be a string but we need a function
# that will be used to filter the files using file_filter # that will be used to filter the files using file_filter
@ -187,19 +191,29 @@ def instantiate_documentloader(class_object, params):
return docs return docs
def instantiate_textsplitter(class_object, params): def instantiate_textsplitter(
class_object,
params: Dict,
):
try: try:
documents = params.pop("documents") documents = params.pop("documents")
except KeyError as e: except KeyError as exc:
raise ValueError( raise ValueError(
"The source you provided did not load correctly or was empty." "The source you provided did not load correctly or was empty."
"Try changing the chunk_size of the Text Splitter." "Try changing the chunk_size of the Text Splitter."
) from e ) from exc
text_splitter = class_object(**params)
if "separator_type" in params and params["separator_type"] == "Text":
text_splitter = class_object(**params)
else:
params["language"] = params.pop("separator_type", None)
params.pop("separators", None)
text_splitter = class_object.from_language(**params)
return text_splitter.split_documents(documents) return text_splitter.split_documents(documents)
def instantiate_utility(node_type, class_object, params): def instantiate_utility(node_type, class_object, params: Dict):
if node_type == "SQLDatabase": if node_type == "SQLDatabase":
return class_object.from_uri(params.pop("uri")) return class_object.from_uri(params.pop("uri"))
return class_object(**params) return class_object(**params)

View file

@ -1,24 +1,23 @@
import os import os
from typing import List
import yaml import yaml
from pydantic import BaseSettings, root_validator from pydantic import BaseSettings, root_validator
class Settings(BaseSettings): class Settings(BaseSettings):
chains: List[str] = [] chains: dict = {}
agents: List[str] = [] agents: dict = {}
prompts: List[str] = [] prompts: dict = {}
llms: List[str] = [] llms: dict = {}
tools: List[str] = [] tools: dict = {}
memories: List[str] = [] memories: dict = {}
embeddings: List[str] = [] embeddings: dict = {}
vectorstores: List[str] = [] vectorstores: dict = {}
documentloaders: List[str] = [] documentloaders: dict = {}
wrappers: List[str] = [] wrappers: dict = {}
toolkits: List[str] = [] toolkits: dict = {}
textsplitters: List[str] = [] textsplitters: dict = {}
utilities: List[str] = [] utilities: dict = {}
dev: bool = False dev: bool = False
database_url: str = "sqlite:///./langflow.db" database_url: str = "sqlite:///./langflow.db"
cache: str = "InMemoryCache" cache: str = "InMemoryCache"
@ -38,16 +37,16 @@ class Settings(BaseSettings):
def update_from_yaml(self, file_path: str, dev: bool = False): def update_from_yaml(self, file_path: str, dev: bool = False):
new_settings = load_settings_from_yaml(file_path) new_settings = load_settings_from_yaml(file_path)
self.chains = new_settings.chains or [] self.chains = new_settings.chains or {}
self.agents = new_settings.agents or [] self.agents = new_settings.agents or {}
self.prompts = new_settings.prompts or [] self.prompts = new_settings.prompts or {}
self.llms = new_settings.llms or [] self.llms = new_settings.llms or {}
self.tools = new_settings.tools or [] self.tools = new_settings.tools or {}
self.memories = new_settings.memories or [] self.memories = new_settings.memories or {}
self.wrappers = new_settings.wrappers or [] self.wrappers = new_settings.wrappers or {}
self.toolkits = new_settings.toolkits or [] self.toolkits = new_settings.toolkits or {}
self.textsplitters = new_settings.textsplitters or [] self.textsplitters = new_settings.textsplitters or {}
self.utilities = new_settings.utilities or [] self.utilities = new_settings.utilities or {}
self.dev = dev self.dev = dev
def update_settings(self, **kwargs): def update_settings(self, **kwargs):

View file

@ -15,14 +15,21 @@ class FrontendNode(BaseModel):
base_classes: List[str] base_classes: List[str]
name: str = "" name: str = ""
display_name: str = "" display_name: str = ""
documentation: str = ""
def set_documentation(self, documentation: str) -> None:
"""Sets the documentation of the frontend node."""
self.documentation = documentation
def to_dict(self) -> dict: def to_dict(self) -> dict:
"""Returns a dict representation of the frontend node."""
return { return {
self.name: { self.name: {
"template": self.template.to_dict(self.format_field), "template": self.template.to_dict(self.format_field),
"description": self.description, "description": self.description,
"base_classes": self.base_classes, "base_classes": self.base_classes,
"display_name": self.display_name or self.name, "display_name": self.display_name or self.name,
"documentation": self.documentation,
}, },
} }

View file

@ -1,5 +1,6 @@
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langchain.text_splitter import Language
class TextSplittersFrontendNode(FrontendNode): class TextSplittersFrontendNode(FrontendNode):
@ -17,6 +18,22 @@ class TextSplittersFrontendNode(FrontendNode):
name = "separator" name = "separator"
elif self.template.type_name == "RecursiveCharacterTextSplitter": elif self.template.type_name == "RecursiveCharacterTextSplitter":
name = "separators" name = "separators"
# Add a field for type of separator
# which will have Text or any value from the
# Language enum
self.template.add_field(
TemplateField(
field_type="str",
required=True,
show=True,
name="separator_type",
advanced=False,
is_list=True,
options=[x.value for x in Language],
value="Text",
display_name="Separator Type",
)
)
self.template.add_field( self.template.add_field(
TemplateField( TemplateField(
field_type="str", field_type="str",

View file

@ -200,7 +200,7 @@ class VectorStoreFrontendNode(FrontendNode):
self.template.add_field(field) self.template.add_field(field)
def add_extra_base_classes(self) -> None: def add_extra_base_classes(self) -> None:
self.base_classes.append("BaseRetriever") self.base_classes.extend(("BaseRetriever", "VectorStoreRetriever"))
@staticmethod @staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:

View file

@ -6,16 +6,7 @@ import {
} from "../../utils"; } from "../../utils";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { import { useContext, useState, useEffect, useRef } from "react";
useContext,
useState,
useEffect,
useRef,
ForwardRefExoticComponent,
ComponentType,
SVGProps,
ReactNode,
} from "react";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
@ -23,10 +14,9 @@ import NodeModal from "../../modals/NodeModal";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
import { NodeToolbar } from "reactflow"; import { NodeToolbar } from "reactflow";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { FileText, Info } from "lucide-react";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import { useSSE } from "../../contexts/SSEContext"; import { useSSE } from "../../contexts/SSEContext";
import { ReactElement } from "react-markdown/lib/react-markdown";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -46,6 +36,7 @@ export default function GenericNode({
const [validationStatus, setValidationStatus] = useState(null); const [validationStatus, setValidationStatus] = useState(null);
// State for outline color // State for outline color
const { sseData, isBuilding } = useSSE(); const { sseData, isBuilding } = useSSE();
const refHtml = useRef(null);
// useEffect(() => { // useEffect(() => {
// if (reactFlowInstance) { // if (reactFlowInstance) {
@ -79,6 +70,22 @@ export default function GenericNode({
useEffect(() => {}, [closePopUp, data.node.template]); useEffect(() => {}, [closePopUp, data.node.template]);
useEffect(() => {
refHtml.current = (
<div className="flex">
<span>{`${data.node.display_name} Documentation`}</span>
<span
className="self-center"
style={{
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
>
<FileText className="h-4 w-4 ml-2" />
</span>
</div>
);
}, []);
return ( return (
<> <>
<NodeToolbar> <NodeToolbar>
@ -103,7 +110,7 @@ export default function GenericNode({
color: nodeColors[types[data.type]] ?? nodeColors.unknown, color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}} }}
/> />
<div className="ml-2 truncate"> <div className="ml-2 truncate flex">
<ShadTooltip <ShadTooltip
delayDuration={1500} delayDuration={1500}
content={data.node.display_name} content={data.node.display_name}
@ -112,6 +119,29 @@ export default function GenericNode({
{data.node.display_name} {data.node.display_name}
</div> </div>
</ShadTooltip> </ShadTooltip>
<div className="">
{data.node.documentation !== "" && (
<ShadTooltip
open={true}
delayDuration={1000}
content={refHtml.current}
>
<a
href={data.node.documentation}
target="_blank"
rel="noopener noreferrer"
>
<Info
style={{
color:
nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
className="ml-2 self-center w-4 h-4"
/>
</a>
</ShadTooltip>
)}
</div>
</div> </div>
</div> </div>
<div className="flex gap-3"> <div className="flex gap-3">

View file

@ -192,39 +192,49 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
function processFlowEdges(flow) { function processFlowEdges(flow) {
if(!flow.data || !flow.data.edges) return; if (!flow.data || !flow.data.edges) return;
flow.data.edges.forEach((edge) => { flow.data.edges.forEach((edge) => {
edge.className = ""; edge.className = "";
edge.style = { stroke: "#555555" }; edge.style = { stroke: "#555555" };
}); });
} }
function updateDisplay_name(node:NodeType,template:APIClassType) {
node.data.node.display_name = template["display_name"]?template["display_name"]:node.data.type; function updateDisplay_name(node: NodeType, template: APIClassType) {
node.data.node.display_name = template["display_name"] || node.data.type;
}
function updateNodeDocumentation(node: NodeType, template: APIClassType) {
node.data.node.documentation = template["documentation"];
} }
function processFlowNodes(flow) { function processFlowNodes(flow) {
if(!flow.data || !flow.data.nodes) return; if (!flow.data || !flow.data.nodes) return;
flow.data.nodes.forEach((node:NodeType) => { flow.data.nodes.forEach((node: NodeType) => {
const template = templates[node.data.type]; const template = templates[node.data.type];
if (!template) { if (!template) {
setErrorData({ title: `Unknown node type: ${node.data.type}` }); setErrorData({ title: `Unknown node type: ${node.data.type}` });
return; return;
} }
if (Object.keys(template["template"]).length > 0) { if (Object.keys(template["template"]).length > 0) {
updateDisplay_name(node,template); updateDisplay_name(node, template);
updateNodeBaseClasses(node, template); updateNodeBaseClasses(node, template);
updateNodeEdges(flow, node, template); updateNodeEdges(flow, node, template);
updateNodeDescription(node, template); updateNodeDescription(node, template);
updateNodeTemplate(node, template); updateNodeTemplate(node, template);
updateNodeDocumentation(node, template);
} }
}); });
} }
function updateNodeBaseClasses(node:NodeType,template:APIClassType) { function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
node.data.node.base_classes = template["base_classes"]; node.data.node.base_classes = template["base_classes"];
} }
function updateNodeEdges(flow:FlowType, node:NodeType,template:APIClassType) { function updateNodeEdges(
flow: FlowType,
node: NodeType,
template: APIClassType
) {
flow.data.edges.forEach((edge) => { flow.data.edges.forEach((edge) => {
if (edge.source === node.id) { if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle edge.sourceHandle = edge.sourceHandle
@ -236,11 +246,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}); });
} }
function updateNodeDescription(node:NodeType,template:APIClassType) { function updateNodeDescription(node: NodeType, template: APIClassType) {
node.data.node.description = template["description"]; node.data.node.description = template["description"];
} }
function updateNodeTemplate(node:NodeType,template:APIClassType) { function updateNodeTemplate(node: NodeType, template: APIClassType) {
node.data.node.template = updateTemplate( node.data.node.template = updateTemplate(
template["template"] as unknown as APITemplateType, template["template"] as unknown as APITemplateType,
node.data.node.template as APITemplateType node.data.node.template as APITemplateType

View file

@ -12,6 +12,7 @@ export type APIClassType = {
description: string; description: string;
template: APITemplateType; template: APITemplateType;
display_name: string; display_name: string;
documentation: string;
[key: string]: Array<string> | string | APITemplateType; [key: string]: Array<string> | string | APITemplateType;
}; };
export type TemplateVariableType = { export type TemplateVariableType = {

View file

@ -88,85 +88,6 @@ def test_prompt_template(client: TestClient):
} }
def test_few_shot_prompt_template(client: TestClient):
response = client.get("api/v1/all")
assert response.status_code == 200
json_response = response.json()
prompts = json_response["prompts"]
prompt = prompts["FewShotPromptTemplate"]
template = prompt["template"]
# Test other fields in the template similar to PromptTemplate
assert template["examples"] == {
"required": False,
"placeholder": "",
"show": True,
"multiline": True,
"password": False,
"name": "examples",
"type": "prompt",
"list": True,
"advanced": False,
}
assert template["example_selector"] == {
"required": False,
"placeholder": "",
"show": False,
"multiline": False,
"password": False,
"name": "example_selector",
"type": "BaseExampleSelector",
"list": False,
"advanced": False,
}
assert template["example_prompt"] == {
"required": True,
"placeholder": "",
"show": True,
"multiline": False,
"password": False,
"name": "example_prompt",
"type": "PromptTemplate",
"list": False,
"advanced": False,
}
assert template["suffix"] == {
"required": True,
"placeholder": "",
"show": True,
"multiline": True,
"password": False,
"name": "suffix",
"type": "prompt",
"list": False,
"advanced": False,
}
assert template["example_separator"] == {
"required": False,
"placeholder": "",
"show": False,
"multiline": False,
"value": "\n\n",
"password": False,
"name": "example_separator",
"type": "str",
"list": False,
"advanced": False,
}
assert template["prefix"] == {
"required": False,
"placeholder": "",
"show": True,
"multiline": True,
"value": "",
"password": False,
"name": "prefix",
"type": "prompt",
"list": False,
"advanced": False,
}
def test_zero_shot_prompt(client: TestClient): def test_zero_shot_prompt(client: TestClient):
response = client.get("api/v1/all") response = client.get("api/v1/all")
assert response.status_code == 200 assert response.status_code == 200