Merge remote-tracking branch 'origin/dev' into feature/store
This commit is contained in:
commit
60f6d08fc3
123 changed files with 2288 additions and 1124 deletions
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Optional
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic import field_validator, BaseModel
|
||||
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langchain.prompts import PromptTemplate
|
||||
|
|
@ -30,11 +30,13 @@ class CodeValidationResponse(BaseModel):
|
|||
imports: dict
|
||||
function: dict
|
||||
|
||||
@validator("imports")
|
||||
@field_validator("imports")
|
||||
@classmethod
|
||||
def validate_imports(cls, v):
|
||||
return v or {"errors": []}
|
||||
|
||||
@validator("function")
|
||||
@field_validator("function")
|
||||
@classmethod
|
||||
def validate_function(cls, v):
|
||||
return v or {"errors": []}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ from langflow.api.utils import build_input_keys_response
|
|||
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
|
||||
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.services.auth.utils import get_current_active_user, get_current_user
|
||||
from langflow.services.auth.utils import (
|
||||
get_current_active_user,
|
||||
get_current_user_by_jwt,
|
||||
)
|
||||
from langflow.services.cache.utils import update_build_status
|
||||
from loguru import logger
|
||||
from langflow.services.deps import get_chat_service, get_session, get_cache_service
|
||||
|
|
@ -34,8 +37,8 @@ async def chat(
|
|||
):
|
||||
"""Websocket endpoint for chat."""
|
||||
try:
|
||||
user = await get_current_user_by_jwt(token, db)
|
||||
await websocket.accept()
|
||||
user = await get_current_user(token, db)
|
||||
if not user:
|
||||
await websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized")
|
||||
if not user.is_active:
|
||||
|
|
@ -149,6 +152,11 @@ async def stream_build(
|
|||
number_of_nodes = len(graph.nodes)
|
||||
update_build_status(cache_service, flow_id, BuildStatus.IN_PROGRESS)
|
||||
|
||||
try:
|
||||
user_id = cache_service[flow_id]["user_id"]
|
||||
except KeyError:
|
||||
logger.debug("No user_id found in cache_service")
|
||||
user_id = None
|
||||
for i, vertex in enumerate(graph.generator_build(), 1):
|
||||
try:
|
||||
log_dict = {
|
||||
|
|
@ -156,9 +164,9 @@ async def stream_build(
|
|||
}
|
||||
yield str(StreamData(event="log", data=log_dict))
|
||||
if vertex.is_task:
|
||||
vertex = try_running_celery_task(vertex)
|
||||
vertex = try_running_celery_task(vertex, user_id)
|
||||
else:
|
||||
vertex.build()
|
||||
vertex.build(user_id=user_id)
|
||||
params = vertex._built_object_repr()
|
||||
valid = True
|
||||
logger.debug(f"Building node {str(vertex.vertex_type)}")
|
||||
|
|
@ -216,7 +224,7 @@ async def stream_build(
|
|||
raise HTTPException(status_code=500, detail=str(exc))
|
||||
|
||||
|
||||
def try_running_celery_task(vertex):
|
||||
def try_running_celery_task(vertex, user_id):
|
||||
# Try running the task in celery
|
||||
# and set the task_id to the local vertex
|
||||
# if it fails, run the task locally
|
||||
|
|
@ -228,5 +236,5 @@ def try_running_celery_task(vertex):
|
|||
except Exception as exc:
|
||||
logger.debug(f"Error running task in celery: {exc}")
|
||||
vertex.task_id = None
|
||||
vertex.build()
|
||||
vertex.build(user_id=user_id)
|
||||
return vertex
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ def get_version():
|
|||
@router.post("/custom_component", status_code=HTTPStatus.OK)
|
||||
async def custom_component(
|
||||
raw_code: CustomComponentCode,
|
||||
user: User = Depends(get_current_active_user),
|
||||
):
|
||||
from langflow.interface.types import (
|
||||
build_langchain_template_custom_component,
|
||||
|
|
@ -218,4 +219,4 @@ async def custom_component(
|
|||
extractor = CustomComponent(code=raw_code.code)
|
||||
extractor.is_check_valid()
|
||||
|
||||
return build_langchain_template_custom_component(extractor)
|
||||
return build_langchain_template_custom_component(extractor, user_id=user.id)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from langflow.services.database.models.flow import FlowCreate, FlowRead
|
|||
from langflow.services.database.models.user import UserRead
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
class BuildStatus(Enum):
|
||||
|
|
@ -91,7 +91,8 @@ class ChatResponse(ChatMessage):
|
|||
is_bot: bool = True
|
||||
files: list = []
|
||||
|
||||
@validator("type")
|
||||
@field_validator("type")
|
||||
@classmethod
|
||||
def validate_message_type(cls, v):
|
||||
if v not in ["start", "stream", "end", "error", "info", "file"]:
|
||||
raise ValueError("type must be start, stream, end, error, info, or file")
|
||||
|
|
@ -109,12 +110,13 @@ class PromptResponse(ChatMessage):
|
|||
class FileResponse(ChatMessage):
|
||||
"""File response schema."""
|
||||
|
||||
data: Any
|
||||
data: Any = None
|
||||
data_type: str
|
||||
type: str = "file"
|
||||
is_bot: bool = True
|
||||
|
||||
@validator("data_type")
|
||||
@field_validator("data_type")
|
||||
@classmethod
|
||||
def validate_data_type(cls, v):
|
||||
if v not in ["image", "csv"]:
|
||||
raise ValueError("data_type must be image or csv")
|
||||
|
|
|
|||
|
|
@ -20,10 +20,11 @@ class ConversationalAgent(CustomComponent):
|
|||
|
||||
def build_config(self):
|
||||
openai_function_models = [
|
||||
"gpt-3.5-turbo-0613",
|
||||
"gpt-3.5-turbo-16k-0613",
|
||||
"gpt-4-0613",
|
||||
"gpt-4-32k-0613",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-16k",
|
||||
"gpt-4",
|
||||
"gpt-4-32k",
|
||||
]
|
||||
return {
|
||||
"tools": {"is_list": True, "display_name": "Tools"},
|
||||
|
|
|
|||
29
src/backend/langflow/components/chains/ConversationChain.py
Normal file
29
src/backend/langflow/components/chains/ConversationChain.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from langflow import CustomComponent
|
||||
from langchain.chains import ConversationChain
|
||||
from typing import Optional, Union, Callable
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, Chain
|
||||
|
||||
|
||||
class ConversationChainComponent(CustomComponent):
|
||||
display_name = "ConversationChain"
|
||||
description = "Chain to have a conversation and load context from memory."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"prompt": {"display_name": "Prompt"},
|
||||
"llm": {"display_name": "LLM"},
|
||||
"memory": {
|
||||
"display_name": "Memory",
|
||||
"info": "Memory to load context from. If none is provided, a ConversationBufferMemory will be used.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
llm: BaseLanguageModel,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
) -> Union[Chain, Callable]:
|
||||
if memory is None:
|
||||
return ConversationChain(llm=llm)
|
||||
return ConversationChain(llm=llm, memory=memory)
|
||||
30
src/backend/langflow/components/chains/LLMChain.py
Normal file
30
src/backend/langflow/components/chains/LLMChain.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from langflow import CustomComponent
|
||||
from langchain.chains import LLMChain
|
||||
from typing import Optional, Union, Callable
|
||||
from langflow.field_typing import (
|
||||
BasePromptTemplate,
|
||||
BaseLanguageModel,
|
||||
BaseMemory,
|
||||
Chain,
|
||||
)
|
||||
|
||||
|
||||
class LLMChainComponent(CustomComponent):
|
||||
display_name = "LLMChain"
|
||||
description = "Chain to run queries against LLMs"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"prompt": {"display_name": "Prompt"},
|
||||
"llm": {"display_name": "LLM"},
|
||||
"memory": {"display_name": "Memory"},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
prompt: BasePromptTemplate,
|
||||
llm: BaseLanguageModel,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
) -> Union[Chain, Callable]:
|
||||
return LLMChain(prompt=prompt, llm=llm, memory=memory)
|
||||
|
|
@ -8,7 +8,7 @@ from langchain.schema import Document
|
|||
class PromptRunner(CustomComponent):
|
||||
display_name: str = "Prompt Runner"
|
||||
description: str = "Run a Chain with the given PromptTemplate"
|
||||
beta = True
|
||||
beta: bool = True
|
||||
field_config = {
|
||||
"llm": {"display_name": "LLM"},
|
||||
"prompt": {
|
||||
|
|
|
|||
232
src/backend/langflow/components/documentloaders/FileLoader.py
Normal file
232
src/backend/langflow/components/documentloaders/FileLoader.py
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
from langflow import CustomComponent
|
||||
from langchain.schema import Document
|
||||
from typing import Any, Dict, List
|
||||
|
||||
loaders_info: List[Dict[str, Any]] = [
|
||||
{
|
||||
"loader": "AirbyteJSONLoader",
|
||||
"name": "Airbyte JSON (.jsonl)",
|
||||
"import": "langchain.document_loaders.AirbyteJSONLoader",
|
||||
"defaultFor": ["jsonl"],
|
||||
"allowdTypes": ["jsonl"],
|
||||
},
|
||||
{
|
||||
"loader": "JSONLoader",
|
||||
"name": "JSON (.json)",
|
||||
"import": "langchain.document_loaders.JSONLoader",
|
||||
"defaultFor": ["json"],
|
||||
"allowdTypes": ["json"],
|
||||
},
|
||||
{
|
||||
"loader": "BSHTMLLoader",
|
||||
"name": "BeautifulSoup4 HTML (.html, .htm)",
|
||||
"import": "langchain.document_loaders.BSHTMLLoader",
|
||||
"allowdTypes": ["html", "htm"],
|
||||
},
|
||||
{
|
||||
"loader": "CSVLoader",
|
||||
"name": "CSV (.csv)",
|
||||
"import": "langchain.document_loaders.CSVLoader",
|
||||
"defaultFor": ["csv"],
|
||||
"allowdTypes": ["csv"],
|
||||
},
|
||||
{
|
||||
"loader": "CoNLLULoader",
|
||||
"name": "CoNLL-U (.conllu)",
|
||||
"import": "langchain.document_loaders.CoNLLULoader",
|
||||
"defaultFor": ["conllu"],
|
||||
"allowdTypes": ["conllu"],
|
||||
},
|
||||
{
|
||||
"loader": "EverNoteLoader",
|
||||
"name": "EverNote (.enex)",
|
||||
"import": "langchain.document_loaders.EverNoteLoader",
|
||||
"defaultFor": ["enex"],
|
||||
"allowdTypes": ["enex"],
|
||||
},
|
||||
{
|
||||
"loader": "FacebookChatLoader",
|
||||
"name": "Facebook Chat (.json)",
|
||||
"import": "langchain.document_loaders.FacebookChatLoader",
|
||||
"allowdTypes": ["json"],
|
||||
},
|
||||
{
|
||||
"loader": "OutlookMessageLoader",
|
||||
"name": "Outlook Message (.msg)",
|
||||
"import": "langchain.document_loaders.OutlookMessageLoader",
|
||||
"defaultFor": ["msg"],
|
||||
"allowdTypes": ["msg"],
|
||||
},
|
||||
{
|
||||
"loader": "PyPDFLoader",
|
||||
"name": "PyPDF (.pdf)",
|
||||
"import": "langchain.document_loaders.PyPDFLoader",
|
||||
"defaultFor": ["pdf"],
|
||||
"allowdTypes": ["pdf"],
|
||||
},
|
||||
{
|
||||
"loader": "STRLoader",
|
||||
"name": "Subtitle (.str)",
|
||||
"import": "langchain.document_loaders.STRLoader",
|
||||
"defaultFor": ["str"],
|
||||
"allowdTypes": ["str"],
|
||||
},
|
||||
{
|
||||
"loader": "TextLoader",
|
||||
"name": "Text (.txt)",
|
||||
"import": "langchain.document_loaders.TextLoader",
|
||||
"defaultFor": ["txt"],
|
||||
"allowdTypes": ["txt"],
|
||||
},
|
||||
{
|
||||
"loader": "UnstructuredEmailLoader",
|
||||
"name": "Unstructured Email (.eml)",
|
||||
"import": "langchain.document_loaders.UnstructuredEmailLoader",
|
||||
"defaultFor": ["eml"],
|
||||
"allowdTypes": ["eml"],
|
||||
},
|
||||
{
|
||||
"loader": "UnstructuredHTMLLoader",
|
||||
"name": "Unstructured HTML (.html, .htm)",
|
||||
"import": "langchain.document_loaders.UnstructuredHTMLLoader",
|
||||
"defaultFor": ["html", "htm"],
|
||||
"allowdTypes": ["html", "htm"],
|
||||
},
|
||||
{
|
||||
"loader": "UnstructuredMarkdownLoader",
|
||||
"name": "Unstructured Markdown (.md)",
|
||||
"import": "langchain.document_loaders.UnstructuredMarkdownLoader",
|
||||
"defaultFor": ["md"],
|
||||
"allowdTypes": ["md"],
|
||||
},
|
||||
{
|
||||
"loader": "UnstructuredPowerPointLoader",
|
||||
"name": "Unstructured PowerPoint (.pptx)",
|
||||
"import": "langchain.document_loaders.UnstructuredPowerPointLoader",
|
||||
"defaultFor": ["pptx"],
|
||||
"allowdTypes": ["pptx"],
|
||||
},
|
||||
{
|
||||
"loader": "UnstructuredWordLoader",
|
||||
"name": "Unstructured Word (.docx)",
|
||||
"import": "langchain.document_loaders.UnstructuredWordLoader",
|
||||
"defaultFor": ["docx"],
|
||||
"allowdTypes": ["docx"],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class FileLoaderComponent(CustomComponent):
|
||||
display_name: str = "File Loader"
|
||||
description: str = "Generic File Loader"
|
||||
beta = True
|
||||
|
||||
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",
|
||||
"pptx",
|
||||
"docx",
|
||||
],
|
||||
"suffixes": [
|
||||
".json",
|
||||
".txt",
|
||||
".csv",
|
||||
".jsonl",
|
||||
".html",
|
||||
".htm",
|
||||
".conllu",
|
||||
".enex",
|
||||
".msg",
|
||||
".pdf",
|
||||
".srt",
|
||||
".eml",
|
||||
".md",
|
||||
".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) -> Document:
|
||||
file_type = file_path.split(".")[-1]
|
||||
|
||||
# Mapeie o nome do loader selecionado para suas informações
|
||||
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 o loader automaticamente com base na extensão do arquivo
|
||||
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:
|
||||
# Importe o loader dinamicamente
|
||||
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)
|
||||
return result.load()
|
||||
62
src/backend/langflow/components/documentloaders/UrlLoader.py
Normal file
62
src/backend/langflow/components/documentloaders/UrlLoader.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
from typing import List
|
||||
from langflow import CustomComponent
|
||||
from langchain.document_loaders import AZLyricsLoader
|
||||
from langchain.document_loaders import CollegeConfidentialLoader
|
||||
from langchain.document_loaders import GitbookLoader
|
||||
from langchain.document_loaders import HNLoader
|
||||
from langchain.document_loaders import IFixitLoader
|
||||
from langchain.document_loaders import IMSDbLoader
|
||||
from langchain.document_loaders import WebBaseLoader
|
||||
|
||||
|
||||
from langchain.schema import Document
|
||||
|
||||
|
||||
class UrlLoaderComponent(CustomComponent):
|
||||
display_name: str = "Url Loader"
|
||||
description: str = "Generic Url Loader Component"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"web_path": {
|
||||
"display_name": "Url",
|
||||
"required": True,
|
||||
},
|
||||
"loader": {
|
||||
"display_name": "Loader",
|
||||
"is_list": True,
|
||||
"required": True,
|
||||
"options": [
|
||||
"AZLyricsLoader",
|
||||
"CollegeConfidentialLoader",
|
||||
"GitbookLoader",
|
||||
"HNLoader",
|
||||
"IFixitLoader",
|
||||
"IMSDbLoader",
|
||||
"WebBaseLoader",
|
||||
],
|
||||
"value": "WebBaseLoader",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(self, web_path: str, loader: str) -> List[Document]:
|
||||
if loader == "AZLyricsLoader":
|
||||
loader_instance = AZLyricsLoader(web_path=web_path) # type: ignore
|
||||
elif loader == "CollegeConfidentialLoader":
|
||||
loader_instance = CollegeConfidentialLoader(web_path=web_path) # type: ignore
|
||||
elif loader == "GitbookLoader":
|
||||
loader_instance = GitbookLoader(web_page=web_path) # type: ignore
|
||||
elif loader == "HNLoader":
|
||||
loader_instance = HNLoader(web_path=web_path) # type: ignore
|
||||
elif loader == "IFixitLoader":
|
||||
loader_instance = IFixitLoader(web_path=web_path) # type: ignore
|
||||
elif loader == "IMSDbLoader":
|
||||
loader_instance = IMSDbLoader(web_path=web_path) # type: ignore
|
||||
elif loader == "WebBaseLoader":
|
||||
loader_instance = WebBaseLoader(web_path=web_path) # type: ignore
|
||||
|
||||
if loader_instance is None:
|
||||
raise ValueError(f"No loader found for: {web_path}")
|
||||
|
||||
return loader_instance.load()
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
|
||||
from langchain.embeddings import BedrockEmbeddings
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
|
||||
class AmazonBedrockEmeddingsComponent(CustomComponent):
|
||||
"""
|
||||
A custom component for implementing an Embeddings Model using Amazon Bedrock.
|
||||
"""
|
||||
|
||||
display_name: str = "Amazon Bedrock Embeddings"
|
||||
description: str = "Embeddings model from Amazon Bedrock."
|
||||
documentation = "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/bedrock"
|
||||
beta = True
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model_id": {
|
||||
"display_name": "Model Id",
|
||||
"options": ["amazon.titan-embed-text-v1"],
|
||||
},
|
||||
"credentials_profile_name": {"display_name": "Credentials Profile Name"},
|
||||
"endpoint_url": {"display_name": "Bedrock Endpoint URL"},
|
||||
"region_name": {"display_name": "AWS Region"},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
model_id: str = "amazon.titan-embed-text-v1",
|
||||
credentials_profile_name: Optional[str] = None,
|
||||
endpoint_url: Optional[str] = None,
|
||||
region_name: Optional[str] = None,
|
||||
) -> Embeddings:
|
||||
try:
|
||||
output = BedrockEmbeddings(
|
||||
credentials_profile_name=credentials_profile_name,
|
||||
model_id=model_id,
|
||||
endpoint_url=endpoint_url,
|
||||
region_name=region_name,
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to AmazonBedrock API.") from e
|
||||
return output
|
||||
0
src/backend/langflow/components/embeddings/__init__.py
Normal file
0
src/backend/langflow/components/embeddings/__init__.py
Normal file
45
src/backend/langflow/components/llms/AmazonBedrock.py
Normal file
45
src/backend/langflow/components/llms/AmazonBedrock.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
from langchain.llms.bedrock import Bedrock
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
|
||||
class AmazonBedrockComponent(CustomComponent):
|
||||
display_name: str = "Amazon Bedrock"
|
||||
description: str = "LLM model from Amazon Bedrock."
|
||||
|
||||
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",
|
||||
"anthropic.claude-v2",
|
||||
"cohere.command-text-v14",
|
||||
],
|
||||
},
|
||||
"credentials_profile_name": {"display_name": "Credentials Profile Name"},
|
||||
"streaming": {"display_name": "Streaming", "field_type": "bool"},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
model_id: str = "anthropic.claude-instant-v1",
|
||||
credentials_profile_name: Optional[str] = None,
|
||||
) -> BaseLLM:
|
||||
try:
|
||||
output = Bedrock(
|
||||
credentials_profile_name=credentials_profile_name,
|
||||
model_id=model_id,
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to AmazonBedrock API.") from e
|
||||
return output
|
||||
48
src/backend/langflow/components/retrievers/AmazonKendra.py
Normal file
48
src/backend/langflow/components/retrievers/AmazonKendra.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
from langchain.retrievers import AmazonKendraRetriever
|
||||
from langchain.schema import BaseRetriever
|
||||
|
||||
|
||||
class AmazonKendraRetrieverComponent(CustomComponent):
|
||||
display_name: str = "Amazon Kendra Retriever"
|
||||
description: str = "Retriever that uses the Amazon Kendra API."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"index_id": {"display_name": "Index ID"},
|
||||
"region_name": {"display_name": "Region Name"},
|
||||
"credentials_profile_name": {"display_name": "Credentials Profile Name"},
|
||||
"attribute_filter": {
|
||||
"display_name": "Attribute Filter",
|
||||
"field_type": "code",
|
||||
},
|
||||
"top_k": {"display_name": "Top K", "field_type": "int"},
|
||||
"user_context": {
|
||||
"display_name": "User Context",
|
||||
"field_type": "code",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
index_id: str,
|
||||
top_k: int = 3,
|
||||
region_name: Optional[str] = None,
|
||||
credentials_profile_name: Optional[str] = None,
|
||||
attribute_filter: Optional[dict] = None,
|
||||
user_context: Optional[dict] = None,
|
||||
) -> BaseRetriever:
|
||||
try:
|
||||
output = AmazonKendraRetriever(
|
||||
index_id=index_id,
|
||||
top_k=top_k,
|
||||
region_name=region_name,
|
||||
credentials_profile_name=credentials_profile_name,
|
||||
attribute_filter=attribute_filter,
|
||||
user_context=user_context,
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to AmazonKendra API.") from e
|
||||
return output
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
from typing import List, Union
|
||||
from langflow import CustomComponent
|
||||
|
||||
from metaphor_python import Metaphor # type: ignore
|
||||
from langchain.tools import Tool
|
||||
from langchain.agents import tool
|
||||
from langchain.agents.agent_toolkits.base import BaseToolkit
|
||||
from langchain.tools import Tool
|
||||
from metaphor_python import Metaphor # type: ignore
|
||||
|
||||
from langflow import CustomComponent
|
||||
|
||||
|
||||
class MetaphorToolkit(CustomComponent):
|
||||
display_name: str = "Metaphor"
|
||||
description: str = "Metaphor Toolkit"
|
||||
documentation = "https://python.langchain.com/docs/integrations/tools/metaphor_search"
|
||||
beta = True
|
||||
beta: bool = True
|
||||
# api key should be password = True
|
||||
field_config = {
|
||||
"metaphor_api_key": {"display_name": "Metaphor API Key", "password": True},
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class GetRequest(CustomComponent):
|
|||
description: str = "Make a GET request to the given URL."
|
||||
output_types: list[str] = ["Document"]
|
||||
documentation: str = "https://docs.langflow.org/components/utilities#get-request"
|
||||
beta = True
|
||||
beta: bool = True
|
||||
field_config = {
|
||||
"url": {
|
||||
"display_name": "URL",
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
# - **Document:** The Document containing the JSON object.
|
||||
|
||||
from langflow import CustomComponent
|
||||
from langchain.schema import Document
|
||||
from langflow import CustomComponent
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class PostRequest(CustomComponent):
|
|||
description: str = "Make a POST request to the given URL."
|
||||
output_types: list[str] = ["Document"]
|
||||
documentation: str = "https://docs.langflow.org/components/utilities#post-request"
|
||||
beta = True
|
||||
beta: bool = True
|
||||
field_config = {
|
||||
"url": {"display_name": "URL", "info": "The URL to make the request to."},
|
||||
"headers": {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class UpdateRequest(CustomComponent):
|
|||
description: str = "Make a PATCH request to the given URL."
|
||||
output_types: list[str] = ["Document"]
|
||||
documentation: str = "https://docs.langflow.org/components/utilities#update-request"
|
||||
beta = True
|
||||
beta: bool = True
|
||||
field_config = {
|
||||
"url": {"display_name": "URL", "info": "The URL to make the request to."},
|
||||
"headers": {
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ class ChromaComponent(CustomComponent):
|
|||
A custom component for implementing a Vector Store using Chroma.
|
||||
"""
|
||||
|
||||
display_name: str = "Chroma (Custom Component)"
|
||||
display_name: str = "Chroma"
|
||||
description: str = "Implementation of Vector Store using Chroma"
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/chroma"
|
||||
beta = True
|
||||
beta: bool = True
|
||||
|
||||
def build_config(self):
|
||||
"""
|
||||
|
|
|
|||
64
src/backend/langflow/components/vectorstores/Redis.py
Normal file
64
src/backend/langflow/components/vectorstores/Redis.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
|
||||
from langchain.vectorstores.redis import Redis
|
||||
from langchain.schema import Document
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
|
||||
class RedisComponent(CustomComponent):
|
||||
"""
|
||||
A custom component for implementing a Vector Store using Redis.
|
||||
"""
|
||||
|
||||
display_name: str = "Redis"
|
||||
description: str = "Implementation of Vector Store using Redis"
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/redis"
|
||||
beta = True
|
||||
|
||||
def build_config(self):
|
||||
"""
|
||||
Builds the configuration for the component.
|
||||
|
||||
Returns:
|
||||
- dict: A dictionary containing the configuration options for the component.
|
||||
"""
|
||||
return {
|
||||
"index_name": {"display_name": "Index Name", "value": "your_index"},
|
||||
"code": {"show": False, "display_name": "Code"},
|
||||
"documents": {"display_name": "Documents", "is_list": True},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"redis_server_url": {
|
||||
"display_name": "Redis Server Connection String",
|
||||
"advanced": False,
|
||||
},
|
||||
"redis_index_name": {"display_name": "Redis Index", "advanced": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
embedding: Embeddings,
|
||||
redis_server_url: str,
|
||||
redis_index_name: str,
|
||||
documents: Optional[Document] = None,
|
||||
) -> VectorStore:
|
||||
"""
|
||||
Builds the Vector Store or BaseRetriever object.
|
||||
|
||||
Args:
|
||||
- embedding (Embeddings): The embeddings to use for the Vector Store.
|
||||
- documents (Optional[Document]): The documents to use for the Vector Store.
|
||||
- redis_index_name (str): The name of the Redis index.
|
||||
- redis_server_url (str): The URL for the Redis server.
|
||||
|
||||
Returns:
|
||||
- VectorStore: The Vector Store object.
|
||||
"""
|
||||
|
||||
return Redis.from_documents(
|
||||
documents=documents, # type: ignore
|
||||
embedding=embedding,
|
||||
redis_url=redis_server_url,
|
||||
index_name=redis_index_name,
|
||||
)
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
from typing import Optional, Union
|
||||
from langflow import CustomComponent
|
||||
|
||||
from langchain.schema import BaseRetriever, Document
|
||||
from langchain.vectorstores import Vectara
|
||||
from langchain.schema import Document
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from langchain.schema import BaseRetriever
|
||||
|
||||
from langflow import CustomComponent
|
||||
|
||||
|
||||
class VectaraComponent(CustomComponent):
|
||||
|
|
|
|||
76
src/backend/langflow/components/vectorstores/pgvector.py
Normal file
76
src/backend/langflow/components/vectorstores/pgvector.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
from typing import Optional, List
|
||||
from langflow import CustomComponent
|
||||
|
||||
from langchain.vectorstores.pgvector import PGVector
|
||||
from langchain.schema import Document
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
|
||||
class PostgresqlVectorComponent(CustomComponent):
|
||||
"""
|
||||
A custom component for implementing a Vector Store using PostgreSQL.
|
||||
"""
|
||||
|
||||
display_name: str = "PGVector"
|
||||
description: str = "Implementation of Vector Store using PostgreSQL"
|
||||
documentation = (
|
||||
"https://python.langchain.com/docs/integrations/vectorstores/pgvector"
|
||||
)
|
||||
beta = True
|
||||
|
||||
def build_config(self):
|
||||
"""
|
||||
Builds the configuration for the component.
|
||||
|
||||
Returns:
|
||||
- dict: A dictionary containing the configuration options for the component.
|
||||
"""
|
||||
return {
|
||||
"index_name": {"display_name": "Index Name", "value": "your_index"},
|
||||
"code": {"show": True, "display_name": "Code"},
|
||||
"documents": {"display_name": "Documents", "is_list": True},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"pg_server_url": {
|
||||
"display_name": "PostgreSQL Server Connection String",
|
||||
"advanced": False,
|
||||
},
|
||||
"collection_name": {"display_name": "Table", "advanced": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
embedding: Embeddings,
|
||||
pg_server_url: str,
|
||||
collection_name: str,
|
||||
documents: Optional[List[Document]] = None,
|
||||
) -> VectorStore:
|
||||
"""
|
||||
Builds the Vector Store or BaseRetriever object.
|
||||
|
||||
Args:
|
||||
- embedding (Embeddings): The embeddings to use for the Vector Store.
|
||||
- documents (Optional[Document]): The documents to use for the Vector Store.
|
||||
- collection_name (str): The name of the PG table.
|
||||
- pg_server_url (str): The URL for the PG server.
|
||||
|
||||
Returns:
|
||||
- VectorStore: The Vector Store object.
|
||||
"""
|
||||
|
||||
try:
|
||||
if documents is None:
|
||||
return PGVector.from_existing_index(
|
||||
embedding=embedding,
|
||||
collection_name=collection_name,
|
||||
connection_string=pg_server_url,
|
||||
)
|
||||
|
||||
return PGVector.from_documents(
|
||||
embedding=embedding,
|
||||
documents=documents,
|
||||
collection_name=collection_name,
|
||||
connection_string=pg_server_url,
|
||||
)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Failed to build PGVector: {e}")
|
||||
|
|
@ -14,14 +14,14 @@ agents:
|
|||
SQLAgent:
|
||||
documentation: ""
|
||||
chains:
|
||||
LLMChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/foundational/llm_chain"
|
||||
# LLMChain:
|
||||
# documentation: "https://python.langchain.com/docs/modules/chains/foundational/llm_chain"
|
||||
LLMMathChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_math"
|
||||
LLMCheckerChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_checker"
|
||||
ConversationChain:
|
||||
documentation: ""
|
||||
# ConversationChain:
|
||||
# documentation: ""
|
||||
SeriesCharacterChain:
|
||||
documentation: ""
|
||||
MidJourneyPromptChain:
|
||||
|
|
@ -106,6 +106,9 @@ embeddings:
|
|||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/cohere"
|
||||
VertexAIEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/google_vertex_ai_palm"
|
||||
AmazonBedrockEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/bedrock"
|
||||
|
||||
llms:
|
||||
OpenAI:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/openai"
|
||||
|
|
@ -265,8 +268,8 @@ retrievers:
|
|||
# ZepRetriever:
|
||||
# documentation: "https://python.langchain.com/docs/modules/data_connection/retrievers/integrations/zep_memorystore"
|
||||
vectorstores:
|
||||
Chroma:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
|
||||
# Chroma:
|
||||
# documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
|
||||
Qdrant:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
|
||||
Weaviate:
|
||||
|
|
|
|||
|
|
@ -14,25 +14,6 @@
|
|||
# "BaseMemory": BaseMemory,
|
||||
# "BaseChatMemory": BaseChatMemory,
|
||||
# }
|
||||
from .constants import (
|
||||
Tool,
|
||||
PromptTemplate,
|
||||
Chain,
|
||||
BaseChatMemory,
|
||||
BaseLLM,
|
||||
BaseLoader,
|
||||
BaseMemory,
|
||||
BaseOutputParser,
|
||||
BaseRetriever,
|
||||
VectorStore,
|
||||
Embeddings,
|
||||
TextSplitter,
|
||||
Document,
|
||||
AgentExecutor,
|
||||
NestedDict,
|
||||
Data,
|
||||
Object,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"NestedDict",
|
||||
|
|
@ -42,6 +23,7 @@ __all__ = [
|
|||
"Chain",
|
||||
"BaseChatMemory",
|
||||
"BaseLLM",
|
||||
"BaseLanguageModel",
|
||||
"BaseLoader",
|
||||
"BaseMemory",
|
||||
"BaseOutputParser",
|
||||
|
|
@ -52,4 +34,7 @@ __all__ = [
|
|||
"Document",
|
||||
"AgentExecutor",
|
||||
"Object",
|
||||
"Callable",
|
||||
"BasePromptTemplate",
|
||||
"ChatPromptTemplate",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Dict, Union
|
||||
from typing import Callable, Dict, Union
|
||||
|
||||
from langchain.agents.agent import AgentExecutor
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.document_loaders.base import BaseLoader
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.llms.base import BaseLanguageModel, BaseLLM
|
||||
from langchain.memory.chat_memory import BaseChatMemory
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.prompts import BasePromptTemplate, ChatPromptTemplate, PromptTemplate
|
||||
from langchain.schema import BaseOutputParser, BaseRetriever, Document
|
||||
from langchain.schema.embeddings import Embeddings
|
||||
from langchain.schema.memory import BaseMemory
|
||||
|
|
@ -30,7 +30,10 @@ LANGCHAIN_BASE_TYPES = {
|
|||
"AgentExecutor": AgentExecutor,
|
||||
"Tool": Tool,
|
||||
"BaseLLM": BaseLLM,
|
||||
"BaseLanguageModel": BaseLanguageModel,
|
||||
"PromptTemplate": PromptTemplate,
|
||||
"ChatPromptTemplate": ChatPromptTemplate,
|
||||
"BasePromptTemplate": BasePromptTemplate,
|
||||
"BaseLoader": BaseLoader,
|
||||
"Document": Document,
|
||||
"TextSplitter": TextSplitter,
|
||||
|
|
@ -53,4 +56,5 @@ CUSTOM_COMPONENT_SUPPORTED_TYPES = {
|
|||
"NestedDict": NestedDict,
|
||||
"Data": Data,
|
||||
"Object": Object,
|
||||
"Callable": Callable,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,10 @@ class ChainVertex(Vertex):
|
|||
# Temporarily remove the code from the params
|
||||
self.params.pop("code", None)
|
||||
# Check if the chain requires a PromptVertex
|
||||
|
||||
# Temporarily remove "code" from the params
|
||||
self.params.pop("code", None)
|
||||
|
||||
for key, value in self.params.items():
|
||||
if isinstance(value, PromptVertex):
|
||||
# Build the PromptVertex, passing the tools if available
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Dict, List, Optional
|
||||
from typing import ClassVar, Dict, List, Optional
|
||||
|
||||
from langchain.agents import types
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ from langflow.utils.util import build_template_from_class, build_template_from_m
|
|||
class AgentCreator(LangChainTypeCreator):
|
||||
type_name: str = "agents"
|
||||
|
||||
from_method_nodes = {"ZeroShotAgent": "from_llm_and_tools"}
|
||||
from_method_nodes: ClassVar[Dict] = {"ZeroShotAgent": "from_llm_and_tools"}
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> type[AgentFrontendNode]:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ from langchain.agents.agent_toolkits import (
|
|||
)
|
||||
from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
|
||||
from langchain.agents.agent_toolkits.json.toolkit import JsonToolkit
|
||||
from langchain.agents.agent_toolkits.pandas.prompt import PREFIX as PANDAS_PREFIX
|
||||
from langchain.agents.agent_toolkits.pandas.prompt import (
|
||||
from langchain_experimental.agents.agent_toolkits.pandas.prompt import (
|
||||
PREFIX as PANDAS_PREFIX,
|
||||
)
|
||||
from langchain_experimental.agents.agent_toolkits.pandas.prompt import (
|
||||
SUFFIX_WITH_DF as PANDAS_SUFFIX,
|
||||
)
|
||||
from langchain.agents.agent_toolkits.sql.prompt import SQL_PREFIX, SQL_SUFFIX
|
||||
|
|
@ -31,7 +33,7 @@ from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
|||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.memory.chat_memory import BaseChatMemory
|
||||
from langchain.sql_database import SQLDatabase
|
||||
from langchain.tools.python.tool import PythonAstREPLTool
|
||||
from langchain_experimental.tools.python.tool import PythonAstREPLTool
|
||||
from langchain.tools.sql_database.prompt import QUERY_CHECKER
|
||||
from langflow.interface.base import CustomAgentExecutor
|
||||
|
||||
|
|
@ -103,9 +105,9 @@ class CSVAgent(CustomAgentExecutor):
|
|||
tools,
|
||||
prefix=PANDAS_PREFIX,
|
||||
suffix=PANDAS_SUFFIX,
|
||||
input_variables=["df", "input", "agent_scratchpad"],
|
||||
input_variables=["df_head", "input", "agent_scratchpad"],
|
||||
)
|
||||
partial_prompt = prompt.partial(df=str(df.head()))
|
||||
partial_prompt = prompt.partial(df_head=str(df.head()))
|
||||
llm_chain = LLMChain(
|
||||
llm=llm,
|
||||
prompt=partial_prompt,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Dict, List, Optional, Type
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langflow.custom.customs import get_custom_nodes
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
|
|
@ -9,7 +9,7 @@ from langflow.template.frontend_node.chains import ChainFrontendNode
|
|||
from loguru import logger
|
||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||
from langchain import chains
|
||||
from langchain_experimental.sql import SQLDatabaseChain # type: ignore
|
||||
from langchain_experimental.sql import SQLDatabaseChain
|
||||
|
||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ class ChainCreator(LangChainTypeCreator):
|
|||
return ChainFrontendNode
|
||||
|
||||
#! We need to find a better solution for this
|
||||
from_method_nodes = {
|
||||
from_method_nodes: ClassVar[Dict] = {
|
||||
"ConversationalRetrievalChain": "from_llm",
|
||||
"LLMCheckerChain": "from_llm",
|
||||
"SQLDatabaseChain": "from_llm",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain.chains import ConversationChain
|
|||
from langchain.memory.buffer import ConversationBufferMemory
|
||||
from langchain.schema import BaseMemory
|
||||
from langflow.interface.base import CustomChain
|
||||
from pydantic import Field, root_validator
|
||||
from pydantic.v1 import Field, root_validator
|
||||
from langchain.chains.question_answering import load_qa_chain
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import ast
|
||||
from typing import Any, Optional
|
||||
from pydantic import BaseModel
|
||||
from typing import Any, ClassVar, Optional
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from langflow.utils import validate
|
||||
from langflow.interface.custom.code_parser import CodeParser
|
||||
from langflow.utils import validate
|
||||
|
||||
|
||||
class ComponentCodeNullError(HTTPException):
|
||||
|
|
@ -15,16 +15,17 @@ class ComponentFunctionEntrypointNameNullError(HTTPException):
|
|||
pass
|
||||
|
||||
|
||||
class Component(BaseModel):
|
||||
ERROR_CODE_NULL = "Python code must be provided."
|
||||
ERROR_FUNCTION_ENTRYPOINT_NAME_NULL = "The name of the entrypoint function must be provided."
|
||||
class Component:
|
||||
ERROR_CODE_NULL: ClassVar[str] = "Python code must be provided."
|
||||
ERROR_FUNCTION_ENTRYPOINT_NAME_NULL: ClassVar[str] = "The name of the entrypoint function must be provided."
|
||||
|
||||
code: Optional[str]
|
||||
function_entrypoint_name = "build"
|
||||
code: Optional[str] = None
|
||||
_function_entrypoint_name: str = "build"
|
||||
field_config: dict = {}
|
||||
|
||||
def __init__(self, **data):
|
||||
super().__init__(**data)
|
||||
for key, value in data.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def get_code_tree(self, code: str):
|
||||
parser = CodeParser(code)
|
||||
|
|
@ -37,7 +38,7 @@ class Component(BaseModel):
|
|||
detail={"error": self.ERROR_CODE_NULL, "traceback": ""},
|
||||
)
|
||||
|
||||
if not self.function_entrypoint_name:
|
||||
if not self._function_entrypoint_name:
|
||||
raise ComponentFunctionEntrypointNameNullError(
|
||||
status_code=400,
|
||||
detail={
|
||||
|
|
@ -46,7 +47,7 @@ class Component(BaseModel):
|
|||
},
|
||||
)
|
||||
|
||||
return validate.create_function(self.code, self.function_entrypoint_name)
|
||||
return validate.create_function(self.code, self._function_entrypoint_name)
|
||||
|
||||
def build_template_config(self, attributes) -> dict:
|
||||
template_config = {}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
from typing import Any, Callable, List, Optional, Union
|
||||
from typing import Any, Callable, ClassVar, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
import yaml
|
||||
from fastapi import HTTPException
|
||||
|
||||
from langflow.field_typing.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
|
||||
from langflow.interface.custom.component import Component
|
||||
from langflow.interface.custom.directory_reader import DirectoryReader
|
||||
from langflow.services.deps import get_db_service
|
||||
from langflow.interface.custom.utils import extract_inner_type
|
||||
|
||||
from langflow.interface.custom.utils import extract_inner_type, extract_union_types
|
||||
from langflow.services.database.models.flow import Flow
|
||||
from langflow.services.database.utils import session_getter
|
||||
from langflow.services.getters import get_db_service
|
||||
from langflow.utils import validate
|
||||
|
||||
from langflow.services.database.utils import session_getter
|
||||
from langflow.services.database.models.flow import Flow
|
||||
from pydantic import Extra
|
||||
import yaml
|
||||
|
||||
|
||||
class CustomComponent(Component, extra=Extra.allow):
|
||||
code: Optional[str]
|
||||
class CustomComponent(Component):
|
||||
display_name: Optional[str] = "Custom Component"
|
||||
description: Optional[str] = "Custom Component"
|
||||
code: Optional[str] = None
|
||||
field_config: dict = {}
|
||||
code_class_base_inheritance = "CustomComponent"
|
||||
function_entrypoint_name = "build"
|
||||
code_class_base_inheritance: ClassVar[str] = "CustomComponent"
|
||||
function_entrypoint_name: ClassVar[str] = "build"
|
||||
function: Optional[Callable] = None
|
||||
return_type_valid_list = list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())
|
||||
return_type_valid_list: List[str] = list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())
|
||||
repr_value: Optional[Any] = ""
|
||||
user_id: Optional[Union[UUID, str]] = None
|
||||
|
||||
|
|
@ -138,9 +139,7 @@ class CustomComponent(Component, extra=Extra.allow):
|
|||
return [return_type] if return_type in self.return_type_valid_list else []
|
||||
|
||||
# If the return type is a Union, then we need to parse it
|
||||
return_type = return_type.replace("Union", "").replace("[", "").replace("]", "")
|
||||
return_type = return_type.split(",")
|
||||
return_type = [item.strip() for item in return_type]
|
||||
return_type = extract_union_types(return_type)
|
||||
return [item for item in return_type if item in self.return_type_valid_list]
|
||||
|
||||
@property
|
||||
|
|
@ -179,8 +178,7 @@ class CustomComponent(Component, extra=Extra.allow):
|
|||
return validate.create_function(self.code, self.function_entrypoint_name)
|
||||
|
||||
def load_flow(self, flow_id: str, tweaks: Optional[dict] = None) -> Any:
|
||||
from langflow.processing.process import build_sorted_vertices
|
||||
from langflow.processing.process import process_tweaks
|
||||
from langflow.processing.process import build_sorted_vertices, process_tweaks
|
||||
|
||||
db_service = get_db_service()
|
||||
with session_getter(db_service) as session:
|
||||
|
|
@ -189,7 +187,7 @@ class CustomComponent(Component, extra=Extra.allow):
|
|||
raise ValueError(f"Flow {flow_id} not found")
|
||||
if tweaks:
|
||||
graph_data = process_tweaks(graph_data=graph_data, tweaks=tweaks)
|
||||
return build_sorted_vertices(graph_data)
|
||||
return build_sorted_vertices(graph_data, self.user_id)
|
||||
|
||||
def list_flows(self, *, get_session: Optional[Callable] = None) -> List[Flow]:
|
||||
if not self.user_id:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ClassCodeDetails(BaseModel):
|
|||
"""
|
||||
|
||||
name: str
|
||||
doc: Optional[str]
|
||||
doc: Optional[str] = None
|
||||
bases: list
|
||||
attributes: list
|
||||
methods: list
|
||||
|
|
@ -23,7 +23,7 @@ class CallableCodeDetails(BaseModel):
|
|||
"""
|
||||
|
||||
name: str
|
||||
doc: Optional[str]
|
||||
doc: Optional[str] = None
|
||||
args: list
|
||||
body: list
|
||||
return_type: Optional[str]
|
||||
return_type: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -8,3 +8,14 @@ def extract_inner_type(return_type: str) -> str:
|
|||
if match := re.match(r"list\[(.*)\]", return_type, re.IGNORECASE):
|
||||
return match[1]
|
||||
return return_type
|
||||
|
||||
|
||||
def extract_union_types(return_type: str) -> list[str]:
|
||||
"""
|
||||
Extracts the inner type from a type hint that is a list.
|
||||
"""
|
||||
# If the return type is a Union, then we need to parse it
|
||||
return_type = return_type.replace("Union", "").replace("[", "").replace("]", "")
|
||||
return_types = return_type.split(",")
|
||||
return_types = [item.strip() for item in return_types]
|
||||
return return_types
|
||||
|
|
|
|||
|
|
@ -1,40 +1,39 @@
|
|||
import json
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type
|
||||
|
||||
import orjson
|
||||
from typing import Any, Callable, Dict, Sequence, Type, TYPE_CHECKING
|
||||
from langchain.schema import Document
|
||||
from langchain.agents import agent as agent_module
|
||||
from langchain.agents.agent import AgentExecutor
|
||||
from langchain.agents.agent_toolkits.base import BaseToolkit
|
||||
from langchain.agents.tools import BaseTool
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.document_loaders.base import BaseLoader
|
||||
from langchain.schema import Document
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from loguru import logger
|
||||
from pydantic import ValidationError
|
||||
|
||||
from langflow.interface.agents.base import agent_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||
from langflow.interface.importing.utils import (
|
||||
get_function,
|
||||
get_function_custom,
|
||||
import_by_type,
|
||||
)
|
||||
from langflow.interface.initialize.llm import initialize_vertexai
|
||||
from langflow.interface.initialize.utils import (
|
||||
handle_format_kwargs,
|
||||
handle_node_type,
|
||||
handle_partial_variables,
|
||||
)
|
||||
|
||||
from langflow.interface.initialize.vector_store import vecstore_initializer
|
||||
|
||||
from pydantic import ValidationError
|
||||
|
||||
from langflow.interface.importing.utils import (
|
||||
get_function,
|
||||
get_function_custom,
|
||||
import_by_type,
|
||||
)
|
||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||
from langflow.interface.agents.base import agent_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.output_parsers.base import output_parser_creator
|
||||
from langflow.interface.retrievers.base import retriever_creator
|
||||
from langflow.interface.wrappers.base import wrapper_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.utils import load_file_into_dict
|
||||
from langflow.interface.wrappers.base import wrapper_creator
|
||||
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 loguru import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow import CustomComponent
|
||||
|
|
@ -279,12 +278,14 @@ def instantiate_embedding(node_type, class_object, params: Dict):
|
|||
try:
|
||||
return class_object(**params)
|
||||
except ValidationError:
|
||||
params = {key: value for key, value in params.items() if key in class_object.__fields__}
|
||||
params = {key: value for key, value in params.items() if key in class_object.model_fields}
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_vectorstore(class_object: Type[VectorStore], params: Dict):
|
||||
search_kwargs = params.pop("search_kwargs", {})
|
||||
if search_kwargs == {"yourkey": "value"}:
|
||||
search_kwargs = {}
|
||||
# clean up docs or texts to have only documents
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ def initialize_chroma(class_object: Type[Chroma], params: dict):
|
|||
params.pop("documents", None)
|
||||
params.pop("texts", None)
|
||||
params["embedding_function"] = params.pop("embedding")
|
||||
chromadb = class_object(**params)
|
||||
chromadb_instance = class_object(**params)
|
||||
else:
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
|
|
@ -199,10 +199,10 @@ def initialize_chroma(class_object: Type[Chroma], params: dict):
|
|||
if value is None:
|
||||
doc.metadata[key] = ""
|
||||
|
||||
chromadb = class_object.from_documents(**params)
|
||||
chromadb_instance = class_object.from_documents(**params)
|
||||
if persist:
|
||||
chromadb.persist()
|
||||
return chromadb
|
||||
chromadb_instance.persist()
|
||||
return chromadb_instance
|
||||
|
||||
|
||||
def initialize_qdrant(class_object: Type[Qdrant], params: dict):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Dict, List, Optional, Type
|
||||
from typing import ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import memory_type_to_cls_dict
|
||||
|
|
@ -14,7 +14,7 @@ from langflow.custom.customs import get_custom_nodes
|
|||
class MemoryCreator(LangChainTypeCreator):
|
||||
type_name: str = "memories"
|
||||
|
||||
from_method_nodes = {
|
||||
from_method_nodes: ClassVar[Dict] = {
|
||||
"ZepChatMessageHistory": "__init__",
|
||||
"SQLiteEntityStore": "__init__",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Dict, List, Optional, Type
|
||||
from typing import ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langchain import output_parsers
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ from langflow.utils.util import build_template_from_class, build_template_from_m
|
|||
|
||||
class OutputParserCreator(LangChainTypeCreator):
|
||||
type_name: str = "output_parsers"
|
||||
from_method_nodes = {
|
||||
from_method_nodes: ClassVar[Dict] = {
|
||||
"StructuredOutputParser": "from_response_schemas",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Dict, List, Optional, Type
|
||||
|
||||
from langchain.prompts import PromptTemplate
|
||||
from pydantic import root_validator
|
||||
from pydantic.v1 import root_validator
|
||||
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Dict, List, Optional, Type
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langchain import retrievers
|
||||
|
||||
|
|
@ -14,7 +14,10 @@ from langflow.utils.util import build_template_from_method, build_template_from_
|
|||
class RetrieverCreator(LangChainTypeCreator):
|
||||
type_name: str = "retrievers"
|
||||
|
||||
from_method_nodes = {"MultiQueryRetriever": "from_llm", "ZepRetriever": "__init__"}
|
||||
from_method_nodes: ClassVar[Dict] = {
|
||||
"MultiQueryRetriever": "from_llm",
|
||||
"ZepRetriever": "__init__",
|
||||
}
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[RetrieverFrontendNode]:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
from typing import Dict, Tuple
|
||||
from typing import Dict, Tuple, Optional, Union
|
||||
from langflow.graph import Graph
|
||||
from loguru import logger
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
def build_sorted_vertices(data_graph) -> Tuple[Graph, Dict]:
|
||||
def build_sorted_vertices(
|
||||
data_graph, user_id: Optional[Union[str, UUID]] = None
|
||||
) -> Tuple[Graph, Dict]:
|
||||
"""
|
||||
Build langchain object from data_graph.
|
||||
"""
|
||||
|
|
@ -13,7 +16,7 @@ def build_sorted_vertices(data_graph) -> Tuple[Graph, Dict]:
|
|||
sorted_vertices = graph.topological_sort()
|
||||
artifacts = {}
|
||||
for vertex in sorted_vertices:
|
||||
vertex.build()
|
||||
vertex.build(user_id=user_id)
|
||||
if vertex.artifacts:
|
||||
artifacts.update(vertex.artifacts)
|
||||
return graph, artifacts
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from langflow.template.field.base import TemplateField
|
|||
from langflow.template.template.base import Template
|
||||
from langflow.utils import util
|
||||
from langflow.utils.util import build_template_from_class
|
||||
from langflow.utils.logger import logger
|
||||
|
||||
TOOL_INPUTS = {
|
||||
"str": TemplateField(
|
||||
|
|
@ -33,7 +34,7 @@ TOOL_INPUTS = {
|
|||
),
|
||||
"llm": TemplateField(field_type="BaseLanguageModel", required=True, is_list=False, show=True),
|
||||
"func": TemplateField(
|
||||
field_type="function",
|
||||
field_type="Callable",
|
||||
required=True,
|
||||
is_list=False,
|
||||
show=True,
|
||||
|
|
@ -70,7 +71,11 @@ class ToolCreator(LangChainTypeCreator):
|
|||
all_tools = {}
|
||||
|
||||
for tool, tool_fcn in ALL_TOOLS_NAMES.items():
|
||||
tool_params = get_tool_params(tool_fcn)
|
||||
try:
|
||||
tool_params = get_tool_params(tool_fcn)
|
||||
except Exception:
|
||||
logger.error(f"Error getting params for tool {tool}")
|
||||
continue
|
||||
|
||||
tool_name = tool_params.get("name") or tool
|
||||
|
||||
|
|
@ -116,7 +121,7 @@ class ToolCreator(LangChainTypeCreator):
|
|||
elif tool_type in CUSTOM_TOOLS:
|
||||
# Get custom tool params
|
||||
params = self.type_to_loader_dict[name]["params"] # type: ignore
|
||||
base_classes = ["function"]
|
||||
base_classes = ["Callable"]
|
||||
if node := customs.get_custom_nodes("tools").get(tool_type):
|
||||
return node
|
||||
elif tool_type in FILE_TOOLS:
|
||||
|
|
@ -126,10 +131,15 @@ class ToolCreator(LangChainTypeCreator):
|
|||
tool_dict = build_template_from_class(tool_type, OTHER_TOOLS)
|
||||
fields = tool_dict["template"]
|
||||
|
||||
# _type is the only key in fields
|
||||
# return None
|
||||
if len(fields) == 1 and "_type" in fields:
|
||||
return None
|
||||
|
||||
# Pop unnecessary fields and add name
|
||||
fields.pop("_type") # type: ignore
|
||||
fields.pop("return_direct") # type: ignore
|
||||
fields.pop("verbose") # type: ignore
|
||||
fields.pop("return_direct", None) # type: ignore
|
||||
fields.pop("verbose", None) # type: ignore
|
||||
|
||||
tool_params = {
|
||||
"name": fields.pop("name")["value"], # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Callable, Optional
|
||||
from langflow.interface.importing.utils import get_function
|
||||
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic.v1 import BaseModel, validator
|
||||
|
||||
from langflow.utils import validate
|
||||
from langchain.agents.tools import Tool
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import ast
|
||||
import contextlib
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Union, Optional
|
||||
from uuid import UUID
|
||||
from langflow.api.utils import get_new_key
|
||||
from langflow.interface.agents.base import agent_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
|
|
@ -202,7 +203,9 @@ def update_attributes(frontend_node, template_config):
|
|||
frontend_node[attribute] = template_config[attribute]
|
||||
|
||||
|
||||
def build_field_config(custom_component: CustomComponent):
|
||||
def build_field_config(
|
||||
custom_component: CustomComponent, user_id: Optional[Union[str, UUID]] = None
|
||||
):
|
||||
"""Build the field configuration for a custom component"""
|
||||
|
||||
try:
|
||||
|
|
@ -212,7 +215,7 @@ def build_field_config(custom_component: CustomComponent):
|
|||
return {}
|
||||
|
||||
try:
|
||||
return custom_class().build_config()
|
||||
return custom_class(user_id=user_id).build_config()
|
||||
except Exception as exc:
|
||||
logger.error(f"Error while building field config: {str(exc)}")
|
||||
return {}
|
||||
|
|
@ -296,7 +299,9 @@ def add_output_types(frontend_node, return_types: List[str]):
|
|||
frontend_node.get("output_types").append(return_type)
|
||||
|
||||
|
||||
def build_langchain_template_custom_component(custom_component: CustomComponent):
|
||||
def build_langchain_template_custom_component(
|
||||
custom_component: CustomComponent, user_id: Optional[Union[str, UUID]] = None
|
||||
):
|
||||
"""Build a custom component template for the langchain"""
|
||||
try:
|
||||
logger.debug("Building custom component template")
|
||||
|
|
@ -309,7 +314,7 @@ def build_langchain_template_custom_component(custom_component: CustomComponent)
|
|||
|
||||
update_attributes(frontend_node, template_config)
|
||||
logger.debug("Updated attributes")
|
||||
field_config = build_field_config(custom_component)
|
||||
field_config = build_field_config(custom_component, user_id=user_id)
|
||||
logger.debug("Built field config")
|
||||
entrypoint_args = custom_component.get_function_entrypoint_args
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
from typing import Dict, List, Optional, Type
|
||||
|
||||
from langchain import utilities
|
||||
from loguru import logger
|
||||
|
||||
from langflow.custom.customs import get_custom_nodes
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
|
||||
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode
|
||||
from loguru import logger
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
|
|
@ -28,9 +27,14 @@ class UtilityCreator(LangChainTypeCreator):
|
|||
"""
|
||||
if self.type_dict is None:
|
||||
settings_service = get_settings_service()
|
||||
self.type_dict = {
|
||||
utility_name: import_class(f"langchain.utilities.{utility_name}") for utility_name in utilities.__all__
|
||||
}
|
||||
self.type_dict = {}
|
||||
for utility_name in utilities.__all__:
|
||||
try:
|
||||
imported = import_class(f"langchain.utilities.{utility_name}")
|
||||
self.type_dict[utility_name] = imported
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.type_dict["SQLDatabase"] = utilities.SQLDatabase
|
||||
# Filter according to settings.utilities
|
||||
self.type_dict = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Dict, List, Optional
|
||||
from typing import ClassVar, Dict, List, Optional
|
||||
|
||||
from langchain.utilities import requests, sql_database
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ from langflow.utils.util import build_template_from_class, build_template_from_m
|
|||
class WrapperCreator(LangChainTypeCreator):
|
||||
type_name: str = "wrappers"
|
||||
|
||||
from_method_nodes = {"SQLDatabase": "from_uri"}
|
||||
from_method_nodes: ClassVar[Dict] = {"SQLDatabase": "from_uri"}
|
||||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from langflow.services.deps import get_session, get_settings_service
|
|||
from sqlmodel import Session
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
oauth2_login = OAuth2PasswordBearer(tokenUrl="api/v1/login")
|
||||
oauth2_login = OAuth2PasswordBearer(tokenUrl="api/v1/login", auto_error=False)
|
||||
|
||||
API_KEY_NAME = "x-api-key"
|
||||
|
||||
|
|
@ -66,6 +66,30 @@ async def api_key_security(
|
|||
|
||||
|
||||
async def get_current_user(
|
||||
token: str = Security(oauth2_login),
|
||||
query_param: str = Security(api_key_query),
|
||||
header_param: str = Security(api_key_header),
|
||||
db: Session = Depends(get_session),
|
||||
) -> User:
|
||||
if token:
|
||||
return await get_current_user_by_jwt(token, db)
|
||||
else:
|
||||
if not query_param and not header_param:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="An API key must be passed as query or header",
|
||||
)
|
||||
user = await api_key_security(query_param, header_param, db)
|
||||
if user:
|
||||
return user
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Invalid or missing API key",
|
||||
)
|
||||
|
||||
|
||||
async def get_current_user_by_jwt(
|
||||
token: Annotated[str, Depends(oauth2_login)],
|
||||
db: Session = Depends(get_session),
|
||||
) -> User:
|
||||
|
|
|
|||
2
src/backend/langflow/services/cache/utils.py
vendored
2
src/backend/langflow/services/cache/utils.py
vendored
|
|
@ -7,7 +7,7 @@ import tempfile
|
|||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Dict
|
||||
from appdirs import user_cache_dir
|
||||
from platformdirs import user_cache_dir
|
||||
from fastapi import UploadFile
|
||||
from langflow.api.v1.schemas import BuildStatus
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ def orjson_dumps(v, *, default=None, sort_keys=False, indent_2=True):
|
|||
|
||||
|
||||
class SQLModelSerializable(SQLModel):
|
||||
class Config:
|
||||
orm_mode = True
|
||||
json_loads = orjson.loads
|
||||
json_dumps = orjson_dumps
|
||||
# TODO[pydantic]: The following keys were removed: `json_loads`, `json_dumps`.
|
||||
# Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Path: src/backend/langflow/database/models/flow.py
|
||||
|
||||
from langflow.services.database.models.base import SQLModelSerializable
|
||||
from pydantic import validator
|
||||
from pydantic import field_validator
|
||||
|
||||
from sqlmodel import Field, JSON, Column, Relationship
|
||||
from uuid import UUID, uuid4
|
||||
|
|
@ -13,11 +13,11 @@ if TYPE_CHECKING:
|
|||
|
||||
class FlowBase(SQLModelSerializable):
|
||||
name: str = Field(index=True)
|
||||
description: Optional[str] = Field(index=True)
|
||||
description: Optional[str] = Field(index=True, nullable=True, default=None)
|
||||
data: Optional[Dict] = Field(default=None, nullable=True)
|
||||
is_component: Optional[bool] = Field(default=False, nullable=True)
|
||||
|
||||
@validator("data")
|
||||
@field_validator("data")
|
||||
def validate_json(v):
|
||||
if not v:
|
||||
return v
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ def update_user(user_db: Optional[User], user: UserUpdate, db: Session = Depends
|
|||
|
||||
|
||||
def update_user_last_login_at(user_id: UUID, db: Session = Depends(get_session)):
|
||||
user_data = UserUpdate(last_login_at=datetime.now(timezone.utc)) # type: ignore
|
||||
user = get_user_by_id(db, user_id)
|
||||
return update_user(user, user_data, db)
|
||||
try:
|
||||
user_data = UserUpdate(last_login_at=datetime.now(timezone.utc)) # type: ignore
|
||||
user = get_user_by_id(db, user_id)
|
||||
return update_user(user, user_data, db)
|
||||
except Exception:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class User(SQLModelSerializable, table=True):
|
|||
is_superuser: bool = Field(default=False)
|
||||
create_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
last_login_at: Optional[datetime] = Field()
|
||||
last_login_at: Optional[datetime] = Field(nullable=True)
|
||||
api_keys: list["ApiKey"] = Relationship(
|
||||
back_populates="user",
|
||||
sa_relationship_kwargs={"cascade": "delete"},
|
||||
|
|
@ -42,13 +42,13 @@ class UserRead(SQLModel):
|
|||
is_superuser: bool = Field()
|
||||
create_at: datetime = Field()
|
||||
updated_at: datetime = Field()
|
||||
last_login_at: Optional[datetime] = Field()
|
||||
last_login_at: Optional[datetime] = Field(nullable=True)
|
||||
|
||||
|
||||
class UserUpdate(SQLModel):
|
||||
username: Optional[str] = Field()
|
||||
profile_image: Optional[str] = Field()
|
||||
password: Optional[str] = Field()
|
||||
is_active: Optional[bool] = Field()
|
||||
is_superuser: Optional[bool] = Field()
|
||||
last_login_at: Optional[datetime] = Field()
|
||||
username: Optional[str] = None
|
||||
profile_image: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
is_active: Optional[bool] = None
|
||||
is_superuser: Optional[bool] = None
|
||||
last_login_at: Optional[datetime] = None
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class DatabaseService(Service):
|
|||
legacy_tables = ["flowstyle"]
|
||||
|
||||
for table, model in model_mapping.items():
|
||||
expected_columns = list(model.__fields__.keys())
|
||||
expected_columns = list(model.model_fields.keys())
|
||||
|
||||
try:
|
||||
available_columns = [col["name"] for col in inspector.get_columns(table)]
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
import secrets
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import secrets
|
||||
|
||||
from loguru import logger
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import Field, validator
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
from langflow.services.settings.constants import (
|
||||
DEFAULT_SUPERUSER,
|
||||
DEFAULT_SUPERUSER_PASSWORD,
|
||||
)
|
||||
from langflow.services.settings.utils import read_secret_from_file, write_secret_to_file
|
||||
|
||||
from pydantic import BaseSettings, Field, validator
|
||||
from passlib.context import CryptContext
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class AuthSettings(BaseSettings):
|
||||
# Login settings
|
||||
|
|
@ -18,8 +20,7 @@ class AuthSettings(BaseSettings):
|
|||
SECRET_KEY: str = Field(
|
||||
default="",
|
||||
description="Secret key for JWT. If not provided, a random one will be generated.",
|
||||
env="LANGFLOW_SECRET_KEY",
|
||||
allow_mutation=False,
|
||||
frozen=False,
|
||||
)
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
|
||||
|
|
@ -37,7 +38,7 @@ class AuthSettings(BaseSettings):
|
|||
SUPERUSER: str = DEFAULT_SUPERUSER
|
||||
SUPERUSER_PASSWORD: str = DEFAULT_SUPERUSER_PASSWORD
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
pwd_context: CryptContext = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ from typing import Optional, List
|
|||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseSettings, root_validator, validator
|
||||
from pydantic import field_validator, validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from loguru import logger
|
||||
|
||||
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
|
||||
|
|
@ -60,14 +61,14 @@ class Settings(BaseSettings):
|
|||
@validator("CONFIG_DIR", pre=True, allow_reuse=True)
|
||||
def set_langflow_dir(cls, value):
|
||||
if not value:
|
||||
import appdirs
|
||||
from platformdirs import user_cache_dir
|
||||
|
||||
# Define the app name and author
|
||||
app_name = "langflow"
|
||||
app_author = "logspace"
|
||||
|
||||
# Get the cache directory for the application
|
||||
cache_dir = appdirs.user_cache_dir(app_name, app_author)
|
||||
cache_dir = user_cache_dir(app_name, app_author)
|
||||
|
||||
# Create a .langflow directory inside the cache directory
|
||||
value = Path(cache_dir)
|
||||
|
|
@ -112,7 +113,7 @@ class Settings(BaseSettings):
|
|||
|
||||
return value
|
||||
|
||||
@validator("COMPONENTS_PATH", pre=True)
|
||||
@field_validator("COMPONENTS_PATH", mode="before")
|
||||
def set_components_path(cls, value):
|
||||
if os.getenv("LANGFLOW_COMPONENTS_PATH"):
|
||||
logger.debug("Adding LANGFLOW_COMPONENTS_PATH to components_path")
|
||||
|
|
@ -137,17 +138,17 @@ class Settings(BaseSettings):
|
|||
logger.debug(f"Components path: {value}")
|
||||
return value
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
extra = "ignore"
|
||||
env_prefix = "LANGFLOW_"
|
||||
model_config = SettingsConfigDict(
|
||||
validate_assignment=True, extra="ignore", env_prefix="LANGFLOW_"
|
||||
)
|
||||
|
||||
@root_validator(allow_reuse=True)
|
||||
def validate_lists(cls, values):
|
||||
for key, value in values.items():
|
||||
if key != "dev" and not value:
|
||||
values[key] = []
|
||||
return values
|
||||
# @model_validator()
|
||||
# @classmethod
|
||||
# def validate_lists(cls, values):
|
||||
# for key, value in values.items():
|
||||
# if key != "dev" and not value:
|
||||
# values[key] = []
|
||||
# return values
|
||||
|
||||
def update_from_yaml(self, file_path: str, dev: bool = False):
|
||||
new_settings = load_settings_from_yaml(file_path)
|
||||
|
|
@ -221,7 +222,7 @@ def load_settings_from_yaml(file_path: str) -> Settings:
|
|||
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
||||
|
||||
for key in settings_dict:
|
||||
if key not in Settings.__fields__.keys():
|
||||
if key not in Settings.model_fields.keys():
|
||||
raise KeyError(f"Key {key} not found in settings")
|
||||
logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class SettingsService(Service):
|
|||
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
||||
|
||||
for key in settings_dict:
|
||||
if key not in Settings.__fields__.keys():
|
||||
if key not in Settings.model_fields.keys():
|
||||
raise KeyError(f"Key {key} not found in settings")
|
||||
logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
|
||||
|
||||
|
|
|
|||
177
src/backend/langflow/settings.py
Normal file
177
src/backend/langflow/settings.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import contextlib
|
||||
import json
|
||||
import os
|
||||
from typing import Optional, List
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from pydantic import validator, model_validator
|
||||
from pydantic_settings import BaseSettings
|
||||
from langflow.utils.logger import logger
|
||||
|
||||
BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
CHAINS: dict = {}
|
||||
AGENTS: dict = {}
|
||||
PROMPTS: dict = {}
|
||||
LLMS: dict = {}
|
||||
TOOLS: dict = {}
|
||||
MEMORIES: dict = {}
|
||||
EMBEDDINGS: dict = {}
|
||||
VECTORSTORES: dict = {}
|
||||
DOCUMENTLOADERS: dict = {}
|
||||
WRAPPERS: dict = {}
|
||||
RETRIEVERS: dict = {}
|
||||
TOOLKITS: dict = {}
|
||||
TEXTSPLITTERS: dict = {}
|
||||
UTILITIES: dict = {}
|
||||
OUTPUT_PARSERS: dict = {}
|
||||
CUSTOM_COMPONENTS: dict = {}
|
||||
|
||||
DEV: bool = False
|
||||
DATABASE_URL: Optional[str] = None
|
||||
CACHE: str = "InMemoryCache"
|
||||
REMOVE_API_KEYS: bool = False
|
||||
COMPONENTS_PATH: List[str] = []
|
||||
|
||||
@validator("DATABASE_URL", pre=True)
|
||||
def set_database_url(cls, value):
|
||||
if not value:
|
||||
logger.debug(
|
||||
"No database_url provided, trying LANGFLOW_DATABASE_URL env variable"
|
||||
)
|
||||
if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"):
|
||||
value = langflow_database_url
|
||||
logger.debug("Using LANGFLOW_DATABASE_URL env variable.")
|
||||
else:
|
||||
logger.debug("No DATABASE_URL env variable, using sqlite database")
|
||||
value = "sqlite:///./langflow.db"
|
||||
|
||||
return value
|
||||
|
||||
@validator("COMPONENTS_PATH", pre=True)
|
||||
def set_components_path(cls, value):
|
||||
if os.getenv("LANGFLOW_COMPONENTS_PATH"):
|
||||
logger.debug("Adding LANGFLOW_COMPONENTS_PATH to components_path")
|
||||
langflow_component_path = os.getenv("LANGFLOW_COMPONENTS_PATH")
|
||||
if (
|
||||
Path(langflow_component_path).exists()
|
||||
and langflow_component_path not in value
|
||||
):
|
||||
if isinstance(langflow_component_path, list):
|
||||
for path in langflow_component_path:
|
||||
if path not in value:
|
||||
value.append(path)
|
||||
logger.debug(
|
||||
f"Extending {langflow_component_path} to components_path"
|
||||
)
|
||||
elif langflow_component_path not in value:
|
||||
value.append(langflow_component_path)
|
||||
logger.debug(
|
||||
f"Appending {langflow_component_path} to components_path"
|
||||
)
|
||||
|
||||
if not value:
|
||||
value = [BASE_COMPONENTS_PATH]
|
||||
logger.debug("Setting default components path to components_path")
|
||||
elif BASE_COMPONENTS_PATH not in value:
|
||||
value.append(BASE_COMPONENTS_PATH)
|
||||
logger.debug("Adding default components path to components_path")
|
||||
|
||||
logger.debug(f"Components path: {value}")
|
||||
return value
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
extra = "ignore"
|
||||
env_prefix = "LANGFLOW_"
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_lists(cls, values):
|
||||
for key, value in values.items():
|
||||
if key != "dev" and not value:
|
||||
values[key] = []
|
||||
return values
|
||||
|
||||
def update_from_yaml(self, file_path: str, dev: bool = False):
|
||||
new_settings = load_settings_from_yaml(file_path)
|
||||
self.CHAINS = new_settings.CHAINS or {}
|
||||
self.AGENTS = new_settings.AGENTS or {}
|
||||
self.PROMPTS = new_settings.PROMPTS or {}
|
||||
self.LLMS = new_settings.LLMS or {}
|
||||
self.TOOLS = new_settings.TOOLS or {}
|
||||
self.MEMORIES = new_settings.MEMORIES or {}
|
||||
self.WRAPPERS = new_settings.WRAPPERS or {}
|
||||
self.TOOLKITS = new_settings.TOOLKITS or {}
|
||||
self.TEXTSPLITTERS = new_settings.TEXTSPLITTERS or {}
|
||||
self.UTILITIES = new_settings.UTILITIES or {}
|
||||
self.EMBEDDINGS = new_settings.EMBEDDINGS or {}
|
||||
self.VECTORSTORES = new_settings.VECTORSTORES or {}
|
||||
self.DOCUMENTLOADERS = new_settings.DOCUMENTLOADERS or {}
|
||||
self.RETRIEVERS = new_settings.RETRIEVERS or {}
|
||||
self.OUTPUT_PARSERS = new_settings.OUTPUT_PARSERS or {}
|
||||
self.CUSTOM_COMPONENTS = new_settings.CUSTOM_COMPONENTS or {}
|
||||
self.COMPONENTS_PATH = new_settings.COMPONENTS_PATH or []
|
||||
self.DEV = dev
|
||||
|
||||
def update_settings(self, **kwargs):
|
||||
logger.debug("Updating settings")
|
||||
for key, value in kwargs.items():
|
||||
# value may contain sensitive information, so we don't want to log it
|
||||
if not hasattr(self, key):
|
||||
logger.debug(f"Key {key} not found in settings")
|
||||
continue
|
||||
logger.debug(f"Updating {key}")
|
||||
if isinstance(getattr(self, key), list):
|
||||
# value might be a '[something]' string
|
||||
with contextlib.suppress(json.decoder.JSONDecodeError):
|
||||
value = json.loads(str(value))
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, Path):
|
||||
item = str(item)
|
||||
if item not in getattr(self, key):
|
||||
getattr(self, key).append(item)
|
||||
logger.debug(f"Extended {key}")
|
||||
else:
|
||||
if isinstance(value, Path):
|
||||
value = str(value)
|
||||
if value not in getattr(self, key):
|
||||
getattr(self, key).append(value)
|
||||
logger.debug(f"Appended {key}")
|
||||
|
||||
else:
|
||||
setattr(self, key, value)
|
||||
logger.debug(f"Updated {key}")
|
||||
logger.debug(f"{key}: {getattr(self, key)}")
|
||||
|
||||
|
||||
def save_settings_to_yaml(settings: Settings, file_path: str):
|
||||
with open(file_path, "w") as f:
|
||||
settings_dict = settings.dict()
|
||||
yaml.dump(settings_dict, f)
|
||||
|
||||
|
||||
def load_settings_from_yaml(file_path: str) -> Settings:
|
||||
# Check if a string is a valid path or a file name
|
||||
if "/" not in file_path:
|
||||
# Get current path
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
file_path = os.path.join(current_path, file_path)
|
||||
|
||||
with open(file_path, "r") as f:
|
||||
settings_dict = yaml.safe_load(f)
|
||||
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
|
||||
|
||||
for key in settings_dict:
|
||||
if key not in Settings.model_fields.keys():
|
||||
raise KeyError(f"Key {key} not found in settings")
|
||||
logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
|
||||
|
||||
return Settings(**settings_dict)
|
||||
|
||||
|
||||
settings = load_settings_from_yaml("config.yaml")
|
||||
|
|
@ -206,7 +206,7 @@ class InitializeAgentNode(FrontendNode):
|
|||
],
|
||||
)
|
||||
description: str = """Construct a zero shot agent from an LLM and tools."""
|
||||
base_classes: list[str] = ["AgentExecutor", "function"]
|
||||
base_classes: list[str] = ["AgentExecutor", "Callable"]
|
||||
|
||||
def to_dict(self):
|
||||
return super().to_dict()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from collections import defaultdict
|
||||
import re
|
||||
from typing import List, Optional
|
||||
from typing import ClassVar, DefaultDict, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
|
@ -15,10 +15,10 @@ from langflow.utils import constants
|
|||
|
||||
|
||||
class FieldFormatters(BaseModel):
|
||||
formatters = {
|
||||
formatters: ClassVar[Dict] = {
|
||||
"openai_api_key": field_formatters.OpenAIAPIKeyFormatter(),
|
||||
}
|
||||
base_formatters = {
|
||||
base_formatters: ClassVar[Dict] = {
|
||||
"kwargs": field_formatters.KwargsFormatter(),
|
||||
"optional": field_formatters.RemoveOptionalFormatter(),
|
||||
"list": field_formatters.ListTypeFormatter(),
|
||||
|
|
@ -49,7 +49,7 @@ class FrontendNode(BaseModel):
|
|||
name: str = ""
|
||||
display_name: str = ""
|
||||
documentation: str = ""
|
||||
custom_fields: defaultdict = defaultdict(list)
|
||||
custom_fields: Optional[DefaultDict[str, List[str]]] = defaultdict(list)
|
||||
output_types: List[str] = []
|
||||
field_formatters: FieldFormatters = Field(default_factory=FieldFormatters)
|
||||
beta: bool = False
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ class ChainFrontendNode(FrontendNode):
|
|||
field.required = True
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
field.field_type = "BaseLanguageModel" # temporary fix
|
||||
field.is_list = False
|
||||
|
||||
if field.name == "return_source_documents":
|
||||
field.required = False
|
||||
|
|
@ -142,7 +144,7 @@ class SeriesCharacterChainNode(FrontendNode):
|
|||
"Chain",
|
||||
"ConversationChain",
|
||||
"SeriesCharacterChain",
|
||||
"function",
|
||||
"Callable",
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -243,7 +245,7 @@ class CombineDocsChainNode(FrontendNode):
|
|||
],
|
||||
)
|
||||
description: str = """Load question answering chain."""
|
||||
base_classes: list[str] = ["BaseCombineDocumentsChain", "function"]
|
||||
base_classes: list[str] = ["BaseCombineDocumentsChain", "Callable"]
|
||||
|
||||
def to_dict(self):
|
||||
return super().to_dict()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional
|
||||
from typing import ClassVar, Dict, Optional
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ class DocumentLoaderFrontNode(FrontendNode):
|
|||
self.base_classes = ["Document"]
|
||||
self.output_types = ["Document"]
|
||||
|
||||
file_path_templates = {
|
||||
file_path_templates: ClassVar[Dict] = {
|
||||
"AirbyteJSONLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]),
|
||||
"CoNLLULoader": build_file_field(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"CSVLoader": build_file_field(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional
|
||||
from typing import ClassVar, Dict, Optional
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS
|
||||
from langflow.template.frontend_node.formatter.base import FieldFormatter
|
||||
|
|
@ -21,7 +21,7 @@ class OpenAIAPIKeyFormatter(FieldFormatter):
|
|||
|
||||
|
||||
class ModelSpecificFieldFormatter(FieldFormatter):
|
||||
MODEL_DICT = {
|
||||
MODEL_DICT: ClassVar[Dict] = {
|
||||
"OpenAI": OPENAI_MODELS,
|
||||
"ChatOpenAI": CHAT_OPENAI_MODELS,
|
||||
"Anthropic": ANTHROPIC_MODELS,
|
||||
|
|
@ -86,7 +86,7 @@ class UnionTypeFormatter(FieldFormatter):
|
|||
|
||||
|
||||
class SpecialFieldFormatter(FieldFormatter):
|
||||
SPECIAL_FIELD_HANDLERS = {
|
||||
SPECIAL_FIELD_HANDLERS: ClassVar[Dict] = {
|
||||
"allowed_tools": lambda field: "Tool",
|
||||
"max_value_length": lambda field: "int",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class ToolNode(FrontendNode):
|
|||
),
|
||||
TemplateField(
|
||||
name="func",
|
||||
field_type="function",
|
||||
field_type="Callable",
|
||||
required=True,
|
||||
is_list=False,
|
||||
show=True,
|
||||
|
|
@ -135,7 +135,7 @@ class PythonFunctionNode(FrontendNode):
|
|||
],
|
||||
)
|
||||
description: str = "Python function to be executed."
|
||||
base_classes: list[str] = ["function"]
|
||||
base_classes: list[str] = ["Callable"]
|
||||
|
||||
def to_dict(self):
|
||||
return super().to_dict()
|
||||
|
|
|
|||
|
|
@ -6,16 +6,14 @@ OPENAI_MODELS = [
|
|||
"text-ada-001",
|
||||
]
|
||||
CHAT_OPENAI_MODELS = [
|
||||
"gpt-3.5-turbo-0613",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-16k-0613",
|
||||
"gpt-3.5-turbo-16k",
|
||||
"gpt-4-0613",
|
||||
"gpt-4-32k-0613",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4",
|
||||
"gpt-4-32k",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-16k",
|
||||
]
|
||||
|
||||
|
||||
ANTHROPIC_MODELS = [
|
||||
# largest model, ideal for a wide range of more complex tasks.
|
||||
"claude-v1",
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ from typing import Optional
|
|||
from loguru import logger
|
||||
from pathlib import Path
|
||||
from rich.logging import RichHandler
|
||||
from platformdirs import user_cache_dir
|
||||
import os
|
||||
import orjson
|
||||
import appdirs
|
||||
|
||||
|
||||
VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
|
|
@ -50,7 +50,7 @@ def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None):
|
|||
)
|
||||
|
||||
if not log_file:
|
||||
cache_dir = Path(appdirs.user_cache_dir("langflow"))
|
||||
cache_dir = Path(user_cache_dir("langflow"))
|
||||
log_file = cache_dir / "langflow.log"
|
||||
|
||||
log_file = Path(log_file)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct
|
|||
docs = parse(_class.__doc__)
|
||||
|
||||
variables = {"_type": _type}
|
||||
for class_field_items, value in _class.__fields__.items():
|
||||
for class_field_items, value in _class.model_fields.items():
|
||||
if class_field_items in ["callback_manager"]:
|
||||
continue
|
||||
variables[class_field_items] = {}
|
||||
|
|
@ -52,7 +52,7 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct
|
|||
# the output to be a function
|
||||
base_classes = get_base_classes(_class)
|
||||
if add_function:
|
||||
base_classes.append("function")
|
||||
base_classes.append("Callable")
|
||||
|
||||
return {
|
||||
"template": format_dict(variables, name),
|
||||
|
|
@ -100,7 +100,7 @@ def build_template_from_class(name: str, type_to_cls_dict: Dict, add_function: b
|
|||
# Adding function to base classes to allow
|
||||
# the output to be a function
|
||||
if add_function:
|
||||
base_classes.append("function")
|
||||
base_classes.append("Callable")
|
||||
return {
|
||||
"template": format_dict(variables, name),
|
||||
"description": docs.short_description or "",
|
||||
|
|
@ -158,7 +158,7 @@ def build_template_from_method(
|
|||
|
||||
# Adding function to base classes to allow the output to be a function
|
||||
if add_function:
|
||||
base_classes.append("function")
|
||||
base_classes.append("Callable")
|
||||
|
||||
return {
|
||||
"template": format_dict(variables, class_name),
|
||||
|
|
@ -254,6 +254,7 @@ def format_dict(dictionary: Dict[str, Any], class_name: Optional[str] = None) ->
|
|||
_type = remove_optional_wrapper(_type)
|
||||
_type = check_list_type(_type, value)
|
||||
_type = replace_mapping_with_dict(_type)
|
||||
_type = get_type_from_union_literal(_type)
|
||||
|
||||
value["type"] = get_formatted_type(key, _type)
|
||||
value["show"] = should_show_field(value, key)
|
||||
|
|
@ -273,6 +274,15 @@ def format_dict(dictionary: Dict[str, Any], class_name: Optional[str] = None) ->
|
|||
return dictionary
|
||||
|
||||
|
||||
# "Union[Literal['f-string'], Literal['jinja2']]" -> "str"
|
||||
def get_type_from_union_literal(union_literal: str) -> str:
|
||||
# if types are literal strings
|
||||
# the type is a string
|
||||
if "Literal" in union_literal:
|
||||
return "str"
|
||||
return union_literal
|
||||
|
||||
|
||||
def get_type(value: Any) -> Union[str, type]:
|
||||
"""
|
||||
Retrieves the type value from the dictionary.
|
||||
|
|
@ -280,7 +290,8 @@ def get_type(value: Any) -> Union[str, type]:
|
|||
Returns:
|
||||
The type value.
|
||||
"""
|
||||
_type = value["type"]
|
||||
# get "type" or "annotation" from the value
|
||||
_type = value.get("type") or value.get("annotation")
|
||||
|
||||
return _type if isinstance(_type, str) else _type.__name__
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue