Merge remote-tracking branch 'origin/dev' into saveComponent
This commit is contained in:
commit
4f50425807
15 changed files with 195 additions and 75 deletions
|
|
@ -56,6 +56,14 @@ LANGFLOW_REMOVE_API_KEYS=
|
|||
# LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600)
|
||||
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
|
||||
# Example: LANGFLOW_SUPERUSER=admin
|
||||
LANGFLOW_SUPERUSER=
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ langfuse = "^1.0.13"
|
|||
pillow = "^10.0.0"
|
||||
metal-sdk = "^2.2.0"
|
||||
markupsafe = "^2.1.3"
|
||||
numexpr = "^2.8.6"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
from langchain.llms import HuggingFaceEndpoint
|
||||
from langchain.llms.huggingface_endpoint import HuggingFaceEndpoint
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
|
||||
|
|
@ -13,7 +13,6 @@ class HuggingFaceEndpointsComponent(CustomComponent):
|
|||
"endpoint_url": {"display_name": "Endpoint URL", "password": True},
|
||||
"task": {
|
||||
"display_name": "Task",
|
||||
"type": "select",
|
||||
"options": ["text2text-generation", "text-generation", "summarization"],
|
||||
},
|
||||
"huggingfacehub_api_token": {"display_name": "API token", "password": True},
|
||||
|
|
@ -27,7 +26,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
endpoint_url: str,
|
||||
task="text2text-generation",
|
||||
task: str = "text2text-generation",
|
||||
huggingfacehub_api_token: Optional[str] = None,
|
||||
model_kwargs: Optional[dict] = None,
|
||||
) -> BaseLLM:
|
||||
|
|
@ -36,6 +35,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
|
|||
endpoint_url=endpoint_url,
|
||||
task=task,
|
||||
huggingfacehub_api_token=huggingfacehub_api_token,
|
||||
model_kwargs=model_kwargs,
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to HuggingFace Endpoints API.") from e
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
from typing import Union, Dict
|
||||
|
||||
# Type alias for more complex dicts
|
||||
NestedDict = Dict[str, Union[str, Dict]]
|
||||
50
src/backend/langflow/field_typing/constants.py
Normal file
50
src/backend/langflow/field_typing/constants.py
Normal 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,
|
||||
}
|
||||
|
|
@ -216,6 +216,16 @@ class Vertex:
|
|||
}
|
||||
elif isinstance(_value, dict):
|
||||
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:
|
||||
params[key] = value.get("value")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.chains import LLMChain
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.schema import Document
|
||||
from langflow.field_typing import (
|
||||
Tool,
|
||||
PromptTemplate,
|
||||
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"
|
||||
description: str = "Create any custom component you want!"
|
||||
|
||||
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))
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Any, Callable, List, Optional, Union
|
||||
from uuid import UUID
|
||||
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.directory_reader import DirectoryReader
|
||||
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
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from typing import Any, List
|
|||
from langflow.api.utils import get_new_key
|
||||
from langflow.interface.agents.base import agent_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.document_loaders.base import documentloader_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)
|
||||
|
||||
|
||||
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):
|
||||
"""Build a custom component template for the langchain"""
|
||||
try:
|
||||
|
|
@ -314,6 +332,9 @@ def build_langchain_template_custom_component(custom_component: CustomComponent)
|
|||
add_base_classes(
|
||||
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")
|
||||
return frontend_node
|
||||
except Exception as exc:
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ def get_langfuse_callback(trace_id):
|
|||
if langfuse := LangfuseInstance.get():
|
||||
logger.debug("Langfuse credentials found")
|
||||
try:
|
||||
trace = langfuse.trace(CreateTrace(id=trace_id))
|
||||
trace = langfuse.trace(
|
||||
CreateTrace(name="langflow-" + trace_id, id=trace_id)
|
||||
)
|
||||
return trace.getNewHandler()
|
||||
except Exception as exc:
|
||||
logger.error(f"Error initializing langfuse callback: {exc}")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from langflow.template.field.base import TemplateField
|
|||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.template.base import Template
|
||||
from langflow.interface.custom.constants import DEFAULT_CUSTOM_COMPONENT_CODE
|
||||
from typing import Optional
|
||||
|
||||
|
||||
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] = []
|
||||
|
||||
def to_dict(self):
|
||||
|
|
|
|||
|
|
@ -191,7 +191,9 @@ def get_base_classes(cls):
|
|||
"""Get the base classes of a class.
|
||||
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 = []
|
||||
for base in bases:
|
||||
if any(type in base.__module__ for type in ["pydantic", "abc"]):
|
||||
|
|
|
|||
|
|
@ -260,9 +260,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
// simulate a click on the link element to trigger the download
|
||||
link.click();
|
||||
setNoticeData({
|
||||
title: "Warning: Critical data, JSON file may include API keys.",
|
||||
});
|
||||
}
|
||||
|
||||
function downloadFlows() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import IconComponent from "../../components/genericIconComponent";
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { removeApiKeys } from "../../utils/reactflowUtils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
|
@ -11,7 +12,8 @@ import BaseModal from "../baseModal";
|
|||
const ExportModal = forwardRef(
|
||||
(props: { children: ReactNode }, ref): JSX.Element => {
|
||||
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);
|
||||
useEffect(() => {
|
||||
setName(flow!.name);
|
||||
|
|
@ -44,6 +46,7 @@ const ExportModal = forwardRef(
|
|||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
checked={checked}
|
||||
onCheckedChange={(event: boolean) => {
|
||||
setChecked(event);
|
||||
}}
|
||||
|
|
@ -52,18 +55,26 @@ const ExportModal = forwardRef(
|
|||
Save with my API keys
|
||||
</label>
|
||||
</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.Footer>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (checked)
|
||||
if (checked) {
|
||||
downloadFlow(
|
||||
flows.find((flow) => flow.id === tabId)!,
|
||||
name!,
|
||||
description
|
||||
);
|
||||
else
|
||||
setNoticeData({
|
||||
title:
|
||||
"Warning: Critical data, JSON file may include API keys.",
|
||||
});
|
||||
} else
|
||||
downloadFlow(
|
||||
removeApiKeys(flows.find((flow) => flow.id === tabId)!),
|
||||
name!,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue