Merge remote-tracking branch 'origin/dev' into saveComponent

This commit is contained in:
anovazzi1 2023-10-16 17:09:42 -03:00
commit 4f50425807
15 changed files with 195 additions and 75 deletions

View file

@ -56,6 +56,14 @@ LANGFLOW_REMOVE_API_KEYS=
# LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600) # LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600)
LANGFLOW_CACHE_TYPE= LANGFLOW_CACHE_TYPE=
# Auto login
# If set to true then a superuser will be logged in automatically
# and the login page will be skipped, keeping the
# default experience of Langflow
# Values: true, false
# Example: LANGFLOW_AUTO_LOGIN=true
LANGFLOW_AUTO_LOGIN=
# Superuser username # Superuser username
# Example: LANGFLOW_SUPERUSER=admin # Example: LANGFLOW_SUPERUSER=admin
LANGFLOW_SUPERUSER= LANGFLOW_SUPERUSER=

View file

@ -90,6 +90,7 @@ langfuse = "^1.0.13"
pillow = "^10.0.0" pillow = "^10.0.0"
metal-sdk = "^2.2.0" metal-sdk = "^2.2.0"
markupsafe = "^2.1.3" markupsafe = "^2.1.3"
numexpr = "^2.8.6"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]

View file

@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
from langflow import CustomComponent from langflow import CustomComponent
from langchain.llms import HuggingFaceEndpoint from langchain.llms.huggingface_endpoint import HuggingFaceEndpoint
from langchain.llms.base import BaseLLM from langchain.llms.base import BaseLLM
@ -13,7 +13,6 @@ class HuggingFaceEndpointsComponent(CustomComponent):
"endpoint_url": {"display_name": "Endpoint URL", "password": True}, "endpoint_url": {"display_name": "Endpoint URL", "password": True},
"task": { "task": {
"display_name": "Task", "display_name": "Task",
"type": "select",
"options": ["text2text-generation", "text-generation", "summarization"], "options": ["text2text-generation", "text-generation", "summarization"],
}, },
"huggingfacehub_api_token": {"display_name": "API token", "password": True}, "huggingfacehub_api_token": {"display_name": "API token", "password": True},
@ -27,7 +26,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
def build( def build(
self, self,
endpoint_url: str, endpoint_url: str,
task="text2text-generation", task: str = "text2text-generation",
huggingfacehub_api_token: Optional[str] = None, huggingfacehub_api_token: Optional[str] = None,
model_kwargs: Optional[dict] = None, model_kwargs: Optional[dict] = None,
) -> BaseLLM: ) -> BaseLLM:
@ -36,6 +35,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
endpoint_url=endpoint_url, endpoint_url=endpoint_url,
task=task, task=task,
huggingfacehub_api_token=huggingfacehub_api_token, huggingfacehub_api_token=huggingfacehub_api_token,
model_kwargs=model_kwargs,
) )
except Exception as e: except Exception as e:
raise ValueError("Could not connect to HuggingFace Endpoints API.") from e raise ValueError("Could not connect to HuggingFace Endpoints API.") from e

View file

@ -1,3 +1,53 @@
from .base import NestedDict # LANGCHAIN_BASE_TYPES = {
# "Chain": Chain,
# "AgentExecutor": AgentExecutor,
# "Tool": Tool,
# "BaseLLM": BaseLLM,
# "PromptTemplate": PromptTemplate,
# "BaseLoader": BaseLoader,
# "Document": Document,
# "TextSplitter": TextSplitter,
# "VectorStore": VectorStore,
# "Embeddings": Embeddings,
# "BaseRetriever": BaseRetriever,
# "BaseOutputParser": BaseOutputParser,
# "BaseMemory": BaseMemory,
# "BaseChatMemory": BaseChatMemory,
# }
from .constants import (
Tool,
PromptTemplate,
Chain,
BaseChatMemory,
BaseLLM,
BaseLoader,
BaseMemory,
BaseOutputParser,
BaseRetriever,
VectorStore,
Embeddings,
TextSplitter,
Document,
AgentExecutor,
NestedDict,
Data,
)
__all__ = ["NestedDict"] __all__ = [
"NestedDict",
"Data",
"Tool",
"PromptTemplate",
"Chain",
"BaseChatMemory",
"BaseLLM",
"BaseLoader",
"BaseMemory",
"BaseOutputParser",
"BaseRetriever",
"VectorStore",
"Embeddings",
"TextSplitter",
"Document",
"AgentExecutor",
]

View file

@ -1,4 +0,0 @@
from typing import Union, Dict
# Type alias for more complex dicts
NestedDict = Dict[str, Union[str, Dict]]

View file

@ -0,0 +1,50 @@
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.memory.chat_memory import BaseChatMemory
from langchain.prompts import PromptTemplate
from langchain.schema import BaseOutputParser, BaseRetriever, Document
from langchain.schema.embeddings import Embeddings
from langchain.schema.memory import BaseMemory
from langchain.text_splitter import TextSplitter
from langchain.tools import Tool
from langchain.vectorstores.base import VectorStore
from typing import Union, Dict
# Type alias for more complex dicts
NestedDict = Dict[str, Union[str, Dict]]
class Data:
pass
LANGCHAIN_BASE_TYPES = {
"Chain": Chain,
"AgentExecutor": AgentExecutor,
"Tool": Tool,
"BaseLLM": BaseLLM,
"PromptTemplate": PromptTemplate,
"BaseLoader": BaseLoader,
"Document": Document,
"TextSplitter": TextSplitter,
"VectorStore": VectorStore,
"Embeddings": Embeddings,
"BaseRetriever": BaseRetriever,
"BaseOutputParser": BaseOutputParser,
"BaseMemory": BaseMemory,
"BaseChatMemory": BaseChatMemory,
}
# Langchain base types plus Python base types
CUSTOM_COMPONENT_SUPPORTED_TYPES = {
**LANGCHAIN_BASE_TYPES,
"str": str,
"int": int,
"float": float,
"bool": bool,
"list": list,
"dict": dict,
"NestedDict": NestedDict,
"Data": Data,
}

View file

@ -216,6 +216,16 @@ class Vertex:
} }
elif isinstance(_value, dict): elif isinstance(_value, dict):
params[key] = _value params[key] = _value
elif value.get("type") == "int" and value.get("value") is not None:
try:
params[key] = int(value.get("value"))
except ValueError:
params[key] = value.get("value")
elif value.get("type") == "float" and value.get("value") is not None:
try:
params[key] = float(value.get("value"))
except ValueError:
params[key] = value.get("value")
else: else:
params[key] = value.get("value") params[key] = value.get("value")

View file

@ -1,65 +1,33 @@
from langchain.prompts import PromptTemplate
from langchain.chains.base import Chain
from langchain.document_loaders.base import BaseLoader
from langchain.schema.embeddings import Embeddings
from langchain.llms.base import BaseLLM
from langchain.schema import BaseRetriever, Document
from langchain.text_splitter import TextSplitter
from langchain.tools import Tool
from langchain.vectorstores.base import VectorStore
from langchain.schema import BaseOutputParser
from langchain.schema.memory import BaseMemory
from langchain.memory.chat_memory import BaseChatMemory
from langchain.agents.agent import AgentExecutor
LANGCHAIN_BASE_TYPES = {
"Chain": Chain,
"AgentExecutor": AgentExecutor,
"Tool": Tool,
"BaseLLM": BaseLLM,
"PromptTemplate": PromptTemplate,
"BaseLoader": BaseLoader,
"Document": Document,
"TextSplitter": TextSplitter,
"VectorStore": VectorStore,
"Embeddings": Embeddings,
"BaseRetriever": BaseRetriever,
"BaseOutputParser": BaseOutputParser,
"BaseMemory": BaseMemory,
"BaseChatMemory": BaseChatMemory,
}
# Langchain base types plus Python base types
CUSTOM_COMPONENT_SUPPORTED_TYPES = {
**LANGCHAIN_BASE_TYPES,
"str": str,
"int": int,
"float": float,
"bool": bool,
"list": list,
"dict": dict,
}
DEFAULT_CUSTOM_COMPONENT_CODE = """from langflow import CustomComponent DEFAULT_CUSTOM_COMPONENT_CODE = """from langflow import CustomComponent
from langchain.llms.base import BaseLLM from langflow.field_typing import (
from langchain.chains import LLMChain Tool,
from langchain.prompts import PromptTemplate PromptTemplate,
from langchain.schema import Document Chain,
BaseChatMemory,
BaseLLM,
BaseLoader,
BaseMemory,
BaseOutputParser,
BaseRetriever,
VectorStore,
Embeddings,
TextSplitter,
Document,
AgentExecutor,
NestedDict,
Data,
)
import requests
class YourComponent(CustomComponent): class Component(CustomComponent):
display_name: str = "Custom Component" display_name: str = "Custom Component"
description: str = "Create any custom component you want!" description: str = "Create any custom component you want!"
def build_config(self): def build_config(self):
return { "url": { "multiline": True, "required": True } } return {"param": {"display_name": "Parameter"}}
def build(self, param: Data) -> Data:
return param
def build(self, url: str, llm: BaseLLM, prompt: PromptTemplate) -> Document:
response = requests.get(url)
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.run(response.text[:300])
return Document(page_content=str(result))
""" """

View file

@ -1,7 +1,7 @@
from typing import Any, Callable, List, Optional, Union from typing import Any, Callable, List, Optional, Union
from uuid import UUID from uuid import UUID
from fastapi import HTTPException from fastapi import HTTPException
from langflow.interface.custom.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES from langflow.field_typing.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
from langflow.interface.custom.component import Component from langflow.interface.custom.component import Component
from langflow.interface.custom.directory_reader import DirectoryReader from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.services.getters import get_db_service from langflow.services.getters import get_db_service
@ -108,6 +108,9 @@ class CustomComponent(Component, extra=Extra.allow):
), ),
}, },
) )
elif not arg.get("type"):
# Set the type to Data
arg["type"] = "Data"
return args return args
@property @property

View file

@ -4,7 +4,7 @@ from typing import Any, List
from langflow.api.utils import get_new_key from langflow.api.utils import get_new_key
from langflow.interface.agents.base import agent_creator from langflow.interface.agents.base import agent_creator
from langflow.interface.chains.base import chain_creator from langflow.interface.chains.base import chain_creator
from langflow.interface.custom.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES from langflow.field_typing.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
from langflow.interface.custom.utils import extract_inner_type from langflow.interface.custom.utils import extract_inner_type
from langflow.interface.document_loaders.base import documentloader_creator from langflow.interface.document_loaders.base import documentloader_creator
from langflow.interface.embeddings.base import embedding_creator from langflow.interface.embeddings.base import embedding_creator
@ -288,6 +288,24 @@ def add_base_classes(frontend_node, return_types: List[str]):
frontend_node.get("base_classes").append(base_class) frontend_node.get("base_classes").append(base_class)
def add_output_types(frontend_node, return_types: List[str]):
"""Add output types to the frontend node"""
for return_type in return_types:
if return_type not in CUSTOM_COMPONENT_SUPPORTED_TYPES or return_type is None:
raise HTTPException(
status_code=400,
detail={
"error": (
"Invalid return type should be one of: "
f"{list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())}"
),
"traceback": traceback.format_exc(),
},
)
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):
"""Build a custom component template for the langchain""" """Build a custom component template for the langchain"""
try: try:
@ -314,6 +332,9 @@ def build_langchain_template_custom_component(custom_component: CustomComponent)
add_base_classes( add_base_classes(
frontend_node, custom_component.get_function_entrypoint_return_type frontend_node, custom_component.get_function_entrypoint_return_type
) )
add_output_types(
frontend_node, custom_component.get_function_entrypoint_return_type
)
logger.debug("Added base classes") logger.debug("Added base classes")
return frontend_node return frontend_node
except Exception as exc: except Exception as exc:

View file

@ -34,7 +34,9 @@ def get_langfuse_callback(trace_id):
if langfuse := LangfuseInstance.get(): if langfuse := LangfuseInstance.get():
logger.debug("Langfuse credentials found") logger.debug("Langfuse credentials found")
try: try:
trace = langfuse.trace(CreateTrace(id=trace_id)) trace = langfuse.trace(
CreateTrace(name="langflow-" + trace_id, id=trace_id)
)
return trace.getNewHandler() return trace.getNewHandler()
except Exception as exc: except Exception as exc:
logger.error(f"Error initializing langfuse callback: {exc}") logger.error(f"Error initializing langfuse callback: {exc}")

View file

@ -2,6 +2,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.interface.custom.constants import DEFAULT_CUSTOM_COMPONENT_CODE from langflow.interface.custom.constants import DEFAULT_CUSTOM_COMPONENT_CODE
from typing import Optional
class CustomComponentFrontendNode(FrontendNode): class CustomComponentFrontendNode(FrontendNode):
@ -24,7 +25,7 @@ class CustomComponentFrontendNode(FrontendNode):
) )
], ],
) )
description: str = "Create any custom component you want!" description: Optional[str] = None
base_classes: list[str] = [] base_classes: list[str] = []
def to_dict(self): def to_dict(self):

View file

@ -191,7 +191,9 @@ def get_base_classes(cls):
"""Get the base classes of a class. """Get the base classes of a class.
These are used to determine the output of the nodes. These are used to determine the output of the nodes.
""" """
if bases := cls.__bases__:
if hasattr(cls, "__bases__") and cls.__bases__:
bases = cls.__bases__
result = [] result = []
for base in bases: for base in bases:
if any(type in base.__module__ for type in ["pydantic", "abc"]): if any(type in base.__module__ for type in ["pydantic", "abc"]):

View file

@ -260,9 +260,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// simulate a click on the link element to trigger the download // simulate a click on the link element to trigger the download
link.click(); link.click();
setNoticeData({
title: "Warning: Critical data, JSON file may include API keys.",
});
} }
function downloadFlows() { function downloadFlows() {

View file

@ -4,6 +4,7 @@ import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox"; import { Checkbox } from "../../components/ui/checkbox";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants"; import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { removeApiKeys } from "../../utils/reactflowUtils"; import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
@ -11,7 +12,8 @@ import BaseModal from "../baseModal";
const ExportModal = forwardRef( const ExportModal = forwardRef(
(props: { children: ReactNode }, ref): JSX.Element => { (props: { children: ReactNode }, ref): JSX.Element => {
const { flows, tabId, downloadFlow } = useContext(TabsContext); const { flows, tabId, downloadFlow } = useContext(TabsContext);
const [checked, setChecked] = useState(false); const { setNoticeData } = useContext(alertContext);
const [checked, setChecked] = useState(true);
const flow = flows.find((f) => f.id === tabId); const flow = flows.find((f) => f.id === tabId);
useEffect(() => { useEffect(() => {
setName(flow!.name); setName(flow!.name);
@ -44,6 +46,7 @@ const ExportModal = forwardRef(
<div className="mt-3 flex items-center space-x-2"> <div className="mt-3 flex items-center space-x-2">
<Checkbox <Checkbox
id="terms" id="terms"
checked={checked}
onCheckedChange={(event: boolean) => { onCheckedChange={(event: boolean) => {
setChecked(event); setChecked(event);
}} }}
@ -52,18 +55,26 @@ const ExportModal = forwardRef(
Save with my API keys Save with my API keys
</label> </label>
</div> </div>
<span className="text-xs text-destructive">
Caution: Uncheck this box only removes API keys from fields
specifically designated for API keys.
</span>
</BaseModal.Content> </BaseModal.Content>
<BaseModal.Footer> <BaseModal.Footer>
<Button <Button
onClick={() => { onClick={() => {
if (checked) if (checked) {
downloadFlow( downloadFlow(
flows.find((flow) => flow.id === tabId)!, flows.find((flow) => flow.id === tabId)!,
name!, name!,
description description
); );
else setNoticeData({
title:
"Warning: Critical data, JSON file may include API keys.",
});
} else
downloadFlow( downloadFlow(
removeApiKeys(flows.find((flow) => flow.id === tabId)!), removeApiKeys(flows.find((flow) => flow.id === tabId)!),
name!, name!,