Merge branch 'release' into add_params_to_llms
This commit is contained in:
commit
1bad93c02c
100 changed files with 3078 additions and 1057 deletions
|
|
@ -13,8 +13,9 @@
|
|||
<img alt="Github License" src="https://img.shields.io/github/license/logspace-ai/langflow" />
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<a href="https://discord.gg/FUhJnnJ9"><img alt="Discord Server" src="https://dcbadge.vercel.app/api/server/FUhJnnJ9?compact=true&style=flat"/></a>
|
||||
<a href="https://discord.gg/EqksyE2EX9"><img alt="Discord Server" src="https://dcbadge.vercel.app/api/server/EqksyE2EX9?compact=true&style=flat"/></a>
|
||||
<a href="https://huggingface.co/spaces/Logspace/LangFlow"><img src="https://huggingface.co/datasets/huggingface/badges/raw/main/open-in-hf-spaces-sm.svg" alt="HuggingFace Spaces"></a>
|
||||
</p>
|
||||
|
||||
|
|
|
|||
1242
poetry.lock
generated
1242
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow"
|
||||
version = "0.1.6"
|
||||
version = "0.2.5"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Logspace <contact@logspace.ai>"]
|
||||
maintainers = [
|
||||
|
|
@ -23,14 +23,14 @@ langflow = "langflow.__main__:main"
|
|||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.12"
|
||||
fastapi = "^0.97.0"
|
||||
fastapi = "^0.98.0"
|
||||
uvicorn = "^0.22.0"
|
||||
beautifulsoup4 = "^4.11.2"
|
||||
google-search-results = "^2.4.1"
|
||||
google-api-python-client = "^2.79.0"
|
||||
typer = "^0.9.0"
|
||||
gunicorn = "^20.1.0"
|
||||
langchain = "^0.0.202"
|
||||
langchain = "^0.0.218"
|
||||
openai = "^0.27.8"
|
||||
types-pyyaml = "^6.0.12.8"
|
||||
pandas = "^1.5.3"
|
||||
|
|
@ -51,12 +51,13 @@ tiktoken = "~0.4.0"
|
|||
wikipedia = "^1.4.0"
|
||||
langchain-serve = { version = ">0.0.39", optional = true }
|
||||
qdrant-client = "^1.2.0"
|
||||
websockets = "^11.0.3"
|
||||
websockets = "^10.3"
|
||||
weaviate-client = "^3.21.0"
|
||||
jina = "3.15.2"
|
||||
sentence-transformers = "^2.2.2"
|
||||
ctransformers = "^0.2.2"
|
||||
cohere = "^4.6.0"
|
||||
python-multipart = "^0.0.6"
|
||||
sqlmodel = "^0.0.8"
|
||||
faiss-cpu = "^1.7.4"
|
||||
anthropic = "^0.2.10"
|
||||
|
|
@ -64,9 +65,14 @@ orjson = "^3.9.1"
|
|||
multiprocess = "^0.70.14"
|
||||
cachetools = "^5.3.1"
|
||||
types-cachetools = "^5.3.0.5"
|
||||
appdirs = "^1.4.4"
|
||||
pinecone-client = "^2.2.2"
|
||||
supabase = "^1.0.3"
|
||||
pymongo = "^4.4.0"
|
||||
certifi = "^2023.5.7"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^23.1.0"
|
||||
ipykernel = "^6.21.2"
|
||||
mypy = "^1.1.1"
|
||||
|
|
@ -78,6 +84,7 @@ requests = "^2.28.0"
|
|||
pytest-cov = "^4.0.0"
|
||||
pandas-stubs = "^2.0.0.230412"
|
||||
types-pillow = "^9.5.0.2"
|
||||
types-appdirs = "^1.4.3.5"
|
||||
|
||||
|
||||
[tool.poetry.extras]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import sys
|
||||
import time
|
||||
from fastapi import FastAPI
|
||||
import httpx
|
||||
from multiprocess import Process, cpu_count # type: ignore
|
||||
import platform
|
||||
|
|
@ -11,9 +10,7 @@ from rich.panel import Panel
|
|||
from rich import box
|
||||
from rich import print as rprint
|
||||
import typer
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from langflow.main import create_app
|
||||
from langflow.main import setup_app
|
||||
from langflow.settings import settings
|
||||
from langflow.utils.logger import configure, logger
|
||||
import webbrowser
|
||||
|
|
@ -30,6 +27,7 @@ def get_number_of_workers(workers=None):
|
|||
|
||||
def update_settings(
|
||||
config: str,
|
||||
cache: str,
|
||||
dev: bool = False,
|
||||
database_url: Optional[str] = None,
|
||||
remove_api_keys: bool = False,
|
||||
|
|
@ -41,6 +39,8 @@ def update_settings(
|
|||
settings.update_settings(database_url=database_url)
|
||||
if remove_api_keys:
|
||||
settings.update_settings(remove_api_keys=remove_api_keys)
|
||||
if cache:
|
||||
settings.update_settings(cache=cache)
|
||||
|
||||
|
||||
def serve_on_jcloud():
|
||||
|
|
@ -102,6 +102,11 @@ def serve(
|
|||
),
|
||||
log_level: str = typer.Option("critical", help="Logging level."),
|
||||
log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file."),
|
||||
cache: str = typer.Argument(
|
||||
envvar="LANGCHAIN_CACHE",
|
||||
help="Type of cache to use. (InMemoryCache, SQLiteCache)",
|
||||
default="SQLiteCache",
|
||||
),
|
||||
jcloud: bool = typer.Option(False, help="Deploy on Jina AI Cloud"),
|
||||
dev: bool = typer.Option(False, help="Run in development mode (may contain bugs)"),
|
||||
database_url: str = typer.Option(
|
||||
|
|
@ -130,17 +135,15 @@ def serve(
|
|||
|
||||
configure(log_level=log_level, log_file=log_file)
|
||||
update_settings(
|
||||
config, dev=dev, database_url=database_url, remove_api_keys=remove_api_keys
|
||||
config,
|
||||
dev=dev,
|
||||
database_url=database_url,
|
||||
remove_api_keys=remove_api_keys,
|
||||
cache=cache,
|
||||
)
|
||||
# get the directory of the current file
|
||||
if not path:
|
||||
frontend_path = Path(__file__).parent
|
||||
static_files_dir = frontend_path / "frontend"
|
||||
else:
|
||||
static_files_dir = Path(path)
|
||||
|
||||
app = create_app()
|
||||
setup_static_files(app, static_files_dir)
|
||||
# create path object if path is provided
|
||||
static_files_dir: Optional[Path] = Path(path) if path else None
|
||||
app = setup_app(static_files_dir=static_files_dir)
|
||||
# check if port is being used
|
||||
if is_port_in_use(port, host):
|
||||
port = get_free_port(port)
|
||||
|
|
@ -188,29 +191,6 @@ def run_on_windows(host, port, log_level, options, app):
|
|||
run_langflow(host, port, log_level, options, app)
|
||||
|
||||
|
||||
def setup_static_files(app: FastAPI, static_files_dir: Path):
|
||||
"""
|
||||
Setup the static files directory.
|
||||
|
||||
Args:
|
||||
app (FastAPI): FastAPI app.
|
||||
path (str): Path to the static files directory.
|
||||
"""
|
||||
app.mount(
|
||||
"/",
|
||||
StaticFiles(directory=static_files_dir, html=True),
|
||||
name="static",
|
||||
)
|
||||
|
||||
@app.exception_handler(404)
|
||||
async def custom_404_handler(request, __):
|
||||
path = static_files_dir / "index.html"
|
||||
|
||||
if not path.exists():
|
||||
raise RuntimeError(f"File at path {path} does not exist.")
|
||||
return FileResponse(path)
|
||||
|
||||
|
||||
def is_port_in_use(port, host="localhost"):
|
||||
"""
|
||||
Check if a port is in use.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
from typing import Optional
|
||||
from langflow.cache.utils import save_uploaded_file
|
||||
from langflow.database.models.flow import Flow
|
||||
from langflow.processing.process import process_graph_cached, process_tweaks
|
||||
from langflow.utils.logger import logger
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||
|
||||
from langflow.api.v1.schemas import (
|
||||
PredictRequest,
|
||||
PredictResponse,
|
||||
ProcessResponse,
|
||||
UploadFileResponse,
|
||||
)
|
||||
|
||||
from langflow.interface.types import build_langchain_types_dict
|
||||
|
|
@ -22,14 +24,17 @@ def get_all():
|
|||
return build_langchain_types_dict()
|
||||
|
||||
|
||||
@router.post("/predict/{flow_id}", response_model=PredictResponse)
|
||||
async def predict_flow(
|
||||
predict_request: PredictRequest,
|
||||
# For backwards compatibility we will keep the old endpoint
|
||||
@router.post("/predict/{flow_id}", response_model=ProcessResponse)
|
||||
@router.post("/process/{flow_id}", response_model=ProcessResponse)
|
||||
async def process_flow(
|
||||
flow_id: str,
|
||||
inputs: Optional[dict] = None,
|
||||
tweaks: Optional[dict] = None,
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
"""
|
||||
Endpoint to process a message using the flow passed in the bearer token.
|
||||
Endpoint to process an input with a given flow_id.
|
||||
"""
|
||||
|
||||
try:
|
||||
|
|
@ -40,15 +45,14 @@ async def predict_flow(
|
|||
if flow.data is None:
|
||||
raise ValueError(f"Flow {flow_id} has no data")
|
||||
graph_data = flow.data
|
||||
if predict_request.tweaks:
|
||||
if tweaks:
|
||||
try:
|
||||
graph_data = process_tweaks(graph_data, predict_request.tweaks)
|
||||
graph_data = process_tweaks(graph_data, tweaks)
|
||||
except Exception as exc:
|
||||
logger.error(f"Error processing tweaks: {exc}")
|
||||
response = process_graph_cached(graph_data, predict_request.message)
|
||||
return PredictResponse(
|
||||
result=response.get("result", ""),
|
||||
intermediate_steps=response.get("thought", ""),
|
||||
response = process_graph_cached(graph_data, inputs)
|
||||
return ProcessResponse(
|
||||
result=response,
|
||||
)
|
||||
except Exception as e:
|
||||
# Log stack trace
|
||||
|
|
@ -56,6 +60,21 @@ async def predict_flow(
|
|||
raise HTTPException(status_code=500, detail=str(e)) from e
|
||||
|
||||
|
||||
@router.post("/upload/{flow_id}", response_model=UploadFileResponse, status_code=201)
|
||||
async def create_upload_file(file: UploadFile, flow_id: str):
|
||||
# Cache file
|
||||
try:
|
||||
file_path = save_uploaded_file(file.file, folder_name=flow_id)
|
||||
|
||||
return UploadFileResponse(
|
||||
flowId=flow_id,
|
||||
file_path=file_path,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"Error saving file: {exc}")
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
||||
|
||||
# get endpoint to return version of langflow
|
||||
@router.get("/version")
|
||||
def get_version():
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from langflow.database.models.flow import FlowCreate, FlowRead
|
||||
from pydantic import BaseModel, Field, validator
|
||||
|
|
@ -20,34 +21,22 @@ class ExportedFlow(BaseModel):
|
|||
data: GraphData
|
||||
|
||||
|
||||
class PredictRequest(BaseModel):
|
||||
"""Predict request schema."""
|
||||
class InputRequest(BaseModel):
|
||||
input: dict
|
||||
|
||||
message: str
|
||||
|
||||
class TweaksRequest(BaseModel):
|
||||
tweaks: Optional[Dict[str, Dict[str, str]]] = Field(default_factory=dict)
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"message": "Hello, how are you?",
|
||||
"tweaks": {
|
||||
"dndnode_986363f0-4677-4035-9f38-74b94af5dd78": {
|
||||
"name": "A tool name",
|
||||
"description": "A tool description",
|
||||
},
|
||||
"dndnode_986363f0-4677-4035-9f38-74b94af57378": {
|
||||
"template": "A {template}",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateTemplateRequest(BaseModel):
|
||||
template: dict
|
||||
|
||||
|
||||
class PredictResponse(BaseModel):
|
||||
"""Predict response schema."""
|
||||
class ProcessResponse(BaseModel):
|
||||
"""Process response schema."""
|
||||
|
||||
result: str
|
||||
intermediate_steps: str = ""
|
||||
result: dict
|
||||
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
|
|
@ -104,6 +93,13 @@ class BuiltResponse(BaseModel):
|
|||
built: bool
|
||||
|
||||
|
||||
class UploadFileResponse(BaseModel):
|
||||
"""Upload file response schema."""
|
||||
|
||||
flowId: str
|
||||
file_path: Path
|
||||
|
||||
|
||||
class StreamData(BaseModel):
|
||||
event: str
|
||||
data: dict
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import json
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from langflow.api.v1.base import (
|
||||
|
|
@ -9,8 +7,6 @@ from langflow.api.v1.base import (
|
|||
PromptValidationResponse,
|
||||
validate_prompt,
|
||||
)
|
||||
from langflow.graph.vertex.types import VectorStoreVertex
|
||||
from langflow.graph import Graph
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.validate import validate_code
|
||||
|
||||
|
|
@ -37,21 +33,3 @@ def post_validate_prompt(prompt: Prompt):
|
|||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise HTTPException(status_code=500, detail=str(e)) from e
|
||||
|
||||
|
||||
# validate node
|
||||
@router.post("/node/{node_id}", status_code=200)
|
||||
def post_validate_node(node_id: str, data: dict):
|
||||
try:
|
||||
# build graph
|
||||
graph = Graph.from_payload(data)
|
||||
# validate node
|
||||
node = graph.get_node(node_id)
|
||||
if node is None:
|
||||
raise ValueError(f"Node {node_id} not found")
|
||||
if not isinstance(node, VectorStoreVertex):
|
||||
node.build()
|
||||
return json.dumps({"valid": True, "params": str(node._built_object_repr())})
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return json.dumps({"valid": False, "params": str(e)})
|
||||
|
|
|
|||
3
src/backend/langflow/cache/base.py
vendored
3
src/backend/langflow/cache/base.py
vendored
|
|
@ -62,9 +62,6 @@ class BaseCache(abc.ABC):
|
|||
|
||||
Args:
|
||||
key: The key of the item to retrieve.
|
||||
|
||||
Returns:
|
||||
The value associated with the key, or None if the key is not found.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
|
|
|||
51
src/backend/langflow/cache/utils.py
vendored
51
src/backend/langflow/cache/utils.py
vendored
|
|
@ -8,15 +8,17 @@ import tempfile
|
|||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from appdirs import user_cache_dir
|
||||
|
||||
CACHE: Dict[str, Any] = {}
|
||||
|
||||
CACHE_DIR = user_cache_dir("langflow", "langflow")
|
||||
|
||||
|
||||
def create_cache_folder(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# Get the destination folder
|
||||
cache_path = Path(tempfile.gettempdir()) / PREFIX
|
||||
cache_path = Path(CACHE_DIR) / PREFIX
|
||||
|
||||
# Create the destination folder if it doesn't exist
|
||||
os.makedirs(cache_path, exist_ok=True)
|
||||
|
|
@ -118,7 +120,7 @@ def save_binary_file(content: str, file_name: str, accepted_types: list[str]) ->
|
|||
raise ValueError(f"File {file_name} is not accepted")
|
||||
|
||||
# Get the destination folder
|
||||
cache_path = Path(tempfile.gettempdir()) / PREFIX
|
||||
cache_path = Path(CACHE_DIR) / PREFIX
|
||||
if not content:
|
||||
raise ValueError("Please, reload the file in the loader.")
|
||||
data = content.split(",")[1]
|
||||
|
|
@ -132,3 +134,46 @@ def save_binary_file(content: str, file_name: str, accepted_types: list[str]) ->
|
|||
file.write(decoded_bytes)
|
||||
|
||||
return file_path
|
||||
|
||||
|
||||
@create_cache_folder
|
||||
def save_uploaded_file(file, folder_name):
|
||||
"""
|
||||
Save an uploaded file to the specified folder with a hash of its content as the file name.
|
||||
|
||||
Args:
|
||||
file: The uploaded file object.
|
||||
folder_name: The name of the folder to save the file in.
|
||||
|
||||
Returns:
|
||||
The path to the saved file.
|
||||
"""
|
||||
cache_path = Path(CACHE_DIR)
|
||||
folder_path = cache_path / folder_name
|
||||
|
||||
# Create the folder if it doesn't exist
|
||||
if not folder_path.exists():
|
||||
folder_path.mkdir()
|
||||
|
||||
# Create a hash of the file content
|
||||
sha256_hash = hashlib.sha256()
|
||||
# Reset the file cursor to the beginning of the file
|
||||
file.seek(0)
|
||||
# Iterate over the uploaded file in small chunks to conserve memory
|
||||
while chunk := file.read(8192): # Read 8KB at a time (adjust as needed)
|
||||
sha256_hash.update(chunk)
|
||||
|
||||
# Use the hex digest of the hash as the file name
|
||||
hex_dig = sha256_hash.hexdigest()
|
||||
file_name = hex_dig
|
||||
|
||||
# Reset the file cursor to the beginning of the file
|
||||
file.seek(0)
|
||||
|
||||
# Save the file with the hash as its name
|
||||
file_path = folder_path / file_name
|
||||
with open(file_path, "wb") as new_file:
|
||||
while chunk := file.read(8192):
|
||||
new_file.write(chunk)
|
||||
|
||||
return file_path
|
||||
|
|
|
|||
2
src/backend/langflow/chat/config.py
Normal file
2
src/backend/langflow/chat/config.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
class ChatConfig:
|
||||
streaming: bool = True
|
||||
|
|
@ -144,6 +144,8 @@ class ChatManager:
|
|||
if isinstance(msg, FileResponse):
|
||||
if msg.data_type == "image":
|
||||
# Base64 encode the image
|
||||
if isinstance(msg.data, str):
|
||||
continue
|
||||
msg.data = pil_to_base64(msg.data)
|
||||
file_responses.append(msg)
|
||||
if msg.type == "start":
|
||||
|
|
|
|||
|
|
@ -1,136 +1,247 @@
|
|||
---
|
||||
agents:
|
||||
- ZeroShotAgent
|
||||
- JsonAgent
|
||||
- CSVAgent
|
||||
- AgentInitializer
|
||||
- VectorStoreAgent
|
||||
- VectorStoreRouterAgent
|
||||
- SQLAgent
|
||||
ZeroShotAgent:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
|
||||
JsonAgent:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/toolkits/openapi"
|
||||
CSVAgent:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/toolkits/csv"
|
||||
AgentInitializer:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/agent_types/"
|
||||
VectorStoreAgent:
|
||||
documentation: ""
|
||||
VectorStoreRouterAgent:
|
||||
documentation: ""
|
||||
SQLAgent:
|
||||
documentation: ""
|
||||
chains:
|
||||
- LLMChain
|
||||
- LLMMathChain
|
||||
- LLMCheckerChain
|
||||
- ConversationChain
|
||||
- SeriesCharacterChain
|
||||
- MidJourneyPromptChain
|
||||
- TimeTravelGuideChain
|
||||
- SQLDatabaseChain
|
||||
- RetrievalQA
|
||||
- RetrievalQAWithSourcesChain
|
||||
- ConversationalRetrievalChain
|
||||
- CombineDocsChain
|
||||
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: ""
|
||||
SeriesCharacterChain:
|
||||
documentation: ""
|
||||
MidJourneyPromptChain:
|
||||
documentation: ""
|
||||
TimeTravelGuideChain:
|
||||
documentation: ""
|
||||
SQLDatabaseChain:
|
||||
documentation: ""
|
||||
RetrievalQA:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/popular/vector_db_qa"
|
||||
RetrievalQAWithSourcesChain:
|
||||
documentation: ""
|
||||
ConversationalRetrievalChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/popular/chat_vector_db"
|
||||
CombineDocsChain:
|
||||
documentation: ""
|
||||
documentloaders:
|
||||
- AirbyteJSONLoader
|
||||
- CoNLLULoader
|
||||
- CSVLoader
|
||||
- UnstructuredEmailLoader
|
||||
- EverNoteLoader
|
||||
- FacebookChatLoader
|
||||
- GutenbergLoader
|
||||
- BSHTMLLoader
|
||||
- UnstructuredHTMLLoader
|
||||
# - UnstructuredImageLoader # Issue with Python 3.11 (https://github.com/Unstructured-IO/unstructured-inference/issues/83)
|
||||
- UnstructuredMarkdownLoader
|
||||
- PyPDFLoader
|
||||
- UnstructuredPowerPointLoader
|
||||
- SRTLoader
|
||||
- TelegramChatLoader
|
||||
- TextLoader
|
||||
- UnstructuredWordDocumentLoader
|
||||
- WebBaseLoader
|
||||
- AZLyricsLoader
|
||||
- CollegeConfidentialLoader
|
||||
- HNLoader
|
||||
- IFixitLoader
|
||||
- IMSDbLoader
|
||||
- GitbookLoader
|
||||
- ReadTheDocsLoader
|
||||
- SlackDirectoryLoader
|
||||
- NotionDirectoryLoader
|
||||
AirbyteJSONLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/airbyte_json"
|
||||
CoNLLULoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/conll-u"
|
||||
CSVLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/csv"
|
||||
UnstructuredEmailLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/email"
|
||||
EverNoteLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/evernote"
|
||||
FacebookChatLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/facebook_chat"
|
||||
GutenbergLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gutenberg"
|
||||
BSHTMLLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
|
||||
UnstructuredHTMLLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
|
||||
UnstructuredMarkdownLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/markdown"
|
||||
PyPDFLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/pdf"
|
||||
UnstructuredPowerPointLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_powerpoint"
|
||||
SRTLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/subtitle"
|
||||
TelegramChatLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/telegram"
|
||||
TextLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/"
|
||||
UnstructuredWordDocumentLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_word"
|
||||
WebBaseLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/web_base"
|
||||
AZLyricsLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/azlyrics"
|
||||
CollegeConfidentialLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/college_confidential"
|
||||
HNLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/hacker_news"
|
||||
IFixitLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/ifixit"
|
||||
IMSDbLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/imsdb"
|
||||
GitbookLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gitbook"
|
||||
ReadTheDocsLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/readthedocs_documentation"
|
||||
SlackDirectoryLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/slack"
|
||||
NotionDirectoryLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/notion"
|
||||
DirectoryLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/file_directory"
|
||||
GitLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/git"
|
||||
embeddings:
|
||||
- OpenAIEmbeddings
|
||||
- HuggingFaceEmbeddings
|
||||
- CohereEmbeddings
|
||||
OpenAIEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai"
|
||||
HuggingFaceEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers"
|
||||
CohereEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/cohere"
|
||||
llms:
|
||||
- OpenAI
|
||||
# - AzureOpenAI
|
||||
# - AzureChatOpenAI
|
||||
- ChatOpenAI
|
||||
- LlamaCpp
|
||||
- CTransformers
|
||||
- Cohere
|
||||
- Anthropic
|
||||
- ChatAnthropic
|
||||
- HuggingFaceHub
|
||||
OpenAI:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/openai"
|
||||
ChatOpenAI:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai"
|
||||
LlamaCpp:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
|
||||
CTransformers:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
|
||||
Cohere:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
|
||||
Anthropic:
|
||||
documentation: ""
|
||||
ChatAnthropic:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
|
||||
HuggingFaceHub:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/huggingface_hub"
|
||||
memories:
|
||||
- ConversationBufferMemory
|
||||
- ConversationSummaryMemory
|
||||
- ConversationKGMemory
|
||||
ConversationBufferMemory:
|
||||
documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
|
||||
ConversationSummaryMemory:
|
||||
documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
|
||||
ConversationKGMemory:
|
||||
documentation: "https://python.langchain.com/docs/modules/memory/how_to/kg"
|
||||
ConversationBufferWindowMemory:
|
||||
documentation: "https://python.langchain.com/docs/modules/memory/how_to/buffer_window"
|
||||
VectorStoreRetrieverMemory:
|
||||
documentation: "https://python.langchain.com/docs/modules/memory/how_to/vectorstore_retriever_memory"
|
||||
prompts:
|
||||
- PromptTemplate
|
||||
- FewShotPromptTemplate
|
||||
- ZeroShotPrompt
|
||||
PromptTemplate:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
|
||||
ZeroShotPrompt:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
|
||||
textsplitters:
|
||||
- CharacterTextSplitter
|
||||
- RecursiveCharacterTextSplitter
|
||||
# - LatexTextSplitter
|
||||
# - PythonCodeTextSplitter
|
||||
CharacterTextSplitter:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
|
||||
RecursiveCharacterTextSplitter:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter"
|
||||
toolkits:
|
||||
- OpenAPIToolkit
|
||||
- JsonToolkit
|
||||
- VectorStoreInfo
|
||||
- VectorStoreRouterToolkit
|
||||
- VectorStoreToolkit
|
||||
OpenAPIToolkit:
|
||||
documentation: ""
|
||||
JsonToolkit:
|
||||
documentation: ""
|
||||
VectorStoreInfo:
|
||||
documentation: ""
|
||||
VectorStoreRouterToolkit:
|
||||
documentation: ""
|
||||
VectorStoreToolkit:
|
||||
documentation: ""
|
||||
tools:
|
||||
- Search
|
||||
- PAL-MATH
|
||||
- Calculator
|
||||
- Serper Search
|
||||
- Tool
|
||||
- PythonFunctionTool
|
||||
- PythonFunction
|
||||
- JsonSpec
|
||||
- News API
|
||||
- TMDB API
|
||||
- Podcast API
|
||||
- QuerySQLDataBaseTool
|
||||
- InfoSQLDatabaseTool
|
||||
- ListSQLDatabaseTool
|
||||
# - QueryCheckerTool
|
||||
- BingSearchRun
|
||||
- GoogleSearchRun
|
||||
- GoogleSearchResults
|
||||
- GoogleSerperRun
|
||||
- JsonListKeysTool
|
||||
- JsonGetValueTool
|
||||
- PythonREPLTool
|
||||
- PythonAstREPLTool
|
||||
- RequestsGetTool
|
||||
- RequestsPostTool
|
||||
- RequestsPatchTool
|
||||
- RequestsPutTool
|
||||
- RequestsDeleteTool
|
||||
- WikipediaQueryRun
|
||||
- WolframAlphaQueryRun
|
||||
Search:
|
||||
documentation: ""
|
||||
PAL-MATH:
|
||||
documentation: ""
|
||||
Calculator:
|
||||
documentation: ""
|
||||
Serper Search:
|
||||
documentation: ""
|
||||
Tool:
|
||||
documentation: ""
|
||||
PythonFunctionTool:
|
||||
documentation: ""
|
||||
PythonFunction:
|
||||
documentation: ""
|
||||
JsonSpec:
|
||||
documentation: ""
|
||||
News API:
|
||||
documentation: ""
|
||||
TMDB API:
|
||||
documentation: ""
|
||||
Podcast API:
|
||||
documentation: ""
|
||||
QuerySQLDataBaseTool:
|
||||
documentation: ""
|
||||
InfoSQLDatabaseTool:
|
||||
documentation: ""
|
||||
ListSQLDatabaseTool:
|
||||
documentation: ""
|
||||
BingSearchRun:
|
||||
documentation: ""
|
||||
GoogleSearchRun:
|
||||
documentation: ""
|
||||
GoogleSearchResults:
|
||||
documentation: ""
|
||||
GoogleSerperRun:
|
||||
documentation: ""
|
||||
JsonListKeysTool:
|
||||
documentation: ""
|
||||
JsonGetValueTool:
|
||||
documentation: ""
|
||||
PythonREPLTool:
|
||||
documentation: ""
|
||||
PythonAstREPLTool:
|
||||
documentation: ""
|
||||
RequestsGetTool:
|
||||
documentation: ""
|
||||
RequestsPostTool:
|
||||
documentation: ""
|
||||
RequestsPatchTool:
|
||||
documentation: ""
|
||||
RequestsPutTool:
|
||||
documentation: ""
|
||||
RequestsDeleteTool:
|
||||
documentation: ""
|
||||
WikipediaQueryRun:
|
||||
documentation: ""
|
||||
WolframAlphaQueryRun:
|
||||
documentation: ""
|
||||
utilities:
|
||||
- BingSearchAPIWrapper
|
||||
- GoogleSearchAPIWrapper
|
||||
- GoogleSerperAPIWrapper
|
||||
- SearxResults
|
||||
- SearxSearchWrapper
|
||||
- SerpAPIWrapper
|
||||
- WikipediaAPIWrapper
|
||||
- WolframAlphaAPIWrapper
|
||||
# - ZapierNLAWrapper
|
||||
- SQLDatabase
|
||||
BingSearchAPIWrapper:
|
||||
documentation: ""
|
||||
GoogleSearchAPIWrapper:
|
||||
documentation: ""
|
||||
GoogleSerperAPIWrapper:
|
||||
documentation: ""
|
||||
SearxResults:
|
||||
documentation: ""
|
||||
SearxSearchWrapper:
|
||||
documentation: ""
|
||||
SerpAPIWrapper:
|
||||
documentation: ""
|
||||
WikipediaAPIWrapper:
|
||||
documentation: ""
|
||||
WolframAlphaAPIWrapper:
|
||||
documentation: ""
|
||||
vectorstores:
|
||||
- Chroma
|
||||
- Qdrant
|
||||
- Weaviate
|
||||
- FAISS
|
||||
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:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/weaviate"
|
||||
FAISS:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
|
||||
Pinecone:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone"
|
||||
SupabaseVectorStore:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/supabase"
|
||||
MongoDBAtlasVectorSearch:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/mongodb_atlas_vector_search"
|
||||
wrappers:
|
||||
- RequestsWrapper
|
||||
# - ChatPromptTemplate
|
||||
# - SystemMessagePromptTemplate
|
||||
# - HumanMessagePromptTemplate
|
||||
RequestsWrapper:
|
||||
documentation: ""
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ class Graph:
|
|||
def generator_build(self) -> Generator:
|
||||
"""Builds each vertex in the graph and yields it."""
|
||||
sorted_vertices = self.topological_sort()
|
||||
logger.info("Sorted vertices: %s", sorted_vertices)
|
||||
logger.debug("Sorted vertices: %s", sorted_vertices)
|
||||
yield from sorted_vertices
|
||||
|
||||
def get_node_neighbors(self, node: Vertex) -> Dict[Vertex, int]:
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ from langflow.interface.wrappers.base import wrapper_creator
|
|||
from typing import Dict, Type
|
||||
|
||||
|
||||
DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"]
|
||||
|
||||
|
||||
VERTEX_TYPE_MAP: Dict[str, Type[Vertex]] = {
|
||||
**{t: PromptVertex for t in prompt_creator.to_list()},
|
||||
**{t: AgentVertex for t in agent_creator.to_list()},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from langflow.cache import utils as cache_utils
|
||||
from langflow.graph.vertex.constants import DIRECT_TYPES
|
||||
from langflow.interface import loading
|
||||
from langflow.utils.constants import DIRECT_TYPES
|
||||
from langflow.interface.initialize import loading
|
||||
from langflow.interface.listing import ALL_TYPES_DICT
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.util import sync_to_async
|
||||
|
|
@ -90,12 +89,7 @@ class Vertex:
|
|||
# Load the type in value.get('suffixes') using
|
||||
# what is inside value.get('content')
|
||||
# value.get('value') is the file name
|
||||
file_name = value.get("value")
|
||||
content = value.get("content")
|
||||
type_to_load = value.get("suffixes")
|
||||
file_path = cache_utils.save_binary_file(
|
||||
content=content, file_name=file_name, accepted_types=type_to_load
|
||||
)
|
||||
file_path = value.get("file_path")
|
||||
|
||||
params[key] = file_path
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"]
|
||||
|
||||
|
|
|
|||
|
|
@ -106,9 +106,6 @@ class VectorStoreVertex(Vertex):
|
|||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="vectorstores")
|
||||
|
||||
def _built_object_repr(self):
|
||||
return "Vector stores can take time to build. It will build on the first query."
|
||||
|
||||
|
||||
class MemoryVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from langchain.agents import (
|
|||
Tool,
|
||||
ZeroShotAgent,
|
||||
initialize_agent,
|
||||
AgentType,
|
||||
)
|
||||
from langchain.agents.agent_toolkits import (
|
||||
SQLDatabaseToolkit,
|
||||
|
|
@ -192,7 +193,7 @@ class SQLAgent(CustomAgentExecutor):
|
|||
from langchain.tools.sql_database.tool import (
|
||||
InfoSQLDatabaseTool,
|
||||
ListSQLDatabaseTool,
|
||||
QueryCheckerTool,
|
||||
QuerySQLCheckerTool,
|
||||
QuerySQLDataBaseTool,
|
||||
)
|
||||
|
||||
|
|
@ -207,7 +208,7 @@ class SQLAgent(CustomAgentExecutor):
|
|||
QuerySQLDataBaseTool(db=db), # type: ignore
|
||||
InfoSQLDatabaseTool(db=db), # type: ignore
|
||||
ListSQLDatabaseTool(db=db), # type: ignore
|
||||
QueryCheckerTool(db=db, llm_chain=llmchain, llm=llm), # type: ignore
|
||||
QuerySQLCheckerTool(db=db, llm_chain=llmchain, llm=llm), # type: ignore
|
||||
]
|
||||
|
||||
prefix = SQL_PREFIX.format(dialect=toolkit.dialect, top_k=10)
|
||||
|
|
@ -297,6 +298,9 @@ class InitializeAgent(CustomAgentExecutor):
|
|||
agent: str,
|
||||
memory: Optional[BaseChatMemory] = None,
|
||||
):
|
||||
# Find which value in the AgentType enum corresponds to the string
|
||||
# passed in as agent
|
||||
agent = AgentType(agent)
|
||||
return initialize_agent(
|
||||
tools=tools,
|
||||
llm=llm,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,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.utils.logger import logger
|
||||
from langflow.settings import settings
|
||||
|
||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||
|
||||
|
|
@ -15,12 +16,29 @@ from langflow.utils.logger import logger
|
|||
class LangChainTypeCreator(BaseModel, ABC):
|
||||
type_name: str
|
||||
type_dict: Optional[Dict] = None
|
||||
name_docs_dict: Optional[Dict[str, str]] = None
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[FrontendNode]:
|
||||
"""The class type of the FrontendNode created in frontend_node."""
|
||||
return FrontendNode
|
||||
|
||||
@property
|
||||
def docs_map(self) -> Dict[str, str]:
|
||||
"""A dict with the name of the component as key and the documentation link as value."""
|
||||
if self.name_docs_dict is None:
|
||||
try:
|
||||
type_settings = getattr(settings, self.type_name)
|
||||
self.name_docs_dict = {
|
||||
name: value_dict["documentation"]
|
||||
for name, value_dict in type_settings.items()
|
||||
}
|
||||
except AttributeError as exc:
|
||||
logger.error(exc)
|
||||
|
||||
self.name_docs_dict = {}
|
||||
return self.name_docs_dict
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
|
|
@ -68,7 +86,7 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
value=value.get("value", None),
|
||||
suffixes=value.get("suffixes", []),
|
||||
file_types=value.get("fileTypes", []),
|
||||
content=value.get("content", None),
|
||||
file_path=value.get("file_path", None),
|
||||
)
|
||||
for key, value in signature["template"].items()
|
||||
if key != "_type"
|
||||
|
|
@ -83,7 +101,7 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
|
||||
signature.add_extra_fields()
|
||||
signature.add_extra_base_classes()
|
||||
|
||||
signature.set_documentation(self.docs_map.get(name, ""))
|
||||
return signature
|
||||
|
||||
|
||||
|
|
|
|||
0
src/backend/langflow/interface/initialize/__init__.py
Normal file
0
src/backend/langflow/interface/initialize/__init__.py
Normal file
|
|
@ -1,31 +1,26 @@
|
|||
import json
|
||||
from typing import Any, Callable, Dict, Optional
|
||||
from typing import Any, Callable, Dict, Sequence, Type
|
||||
|
||||
from langchain.agents import ZeroShotAgent
|
||||
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.load_tools import (
|
||||
_BASE_TOOLS,
|
||||
_EXTRA_LLM_TOOLS,
|
||||
_EXTRA_OPTIONAL_TOOLS,
|
||||
_LLM_TOOLS,
|
||||
)
|
||||
from langchain.agents.loading import load_agent_from_config
|
||||
from langchain.agents.tools import Tool
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.callbacks.base import BaseCallbackManager
|
||||
from langchain.chains.loading import load_chain_from_config
|
||||
from langchain.llms.loading import load_llm_from_config
|
||||
from langchain.agents.tools import BaseTool
|
||||
|
||||
from langflow.interface.initialize.vector_store import vecstore_initializer
|
||||
|
||||
from pydantic import ValidationError
|
||||
|
||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||
from langflow.interface.importing.utils import get_function, import_by_type
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.types import get_type_list
|
||||
from langflow.interface.utils import load_file_into_dict
|
||||
from langflow.utils import util, validate
|
||||
from langflow.utils import validate
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from langchain.document_loaders.base import BaseLoader
|
||||
from langchain.prompts.base import BasePromptTemplate
|
||||
|
||||
|
||||
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
|
||||
|
|
@ -88,11 +83,11 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_llm(node_type, class_object, params):
|
||||
def instantiate_llm(node_type, class_object, params: Dict):
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_chains(node_type, class_object, params):
|
||||
def instantiate_chains(node_type, class_object: Type[Chain], params: Dict):
|
||||
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
||||
params["retriever"] = params["retriever"].as_retriever()
|
||||
if node_type in chain_creator.from_method_nodes:
|
||||
|
|
@ -104,11 +99,11 @@ def instantiate_chains(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_agent(class_object, params):
|
||||
def instantiate_agent(class_object: Type[agent_module.Agent], params: Dict):
|
||||
return load_agent_executor(class_object, params)
|
||||
|
||||
|
||||
def instantiate_prompt(node_type, class_object, params):
|
||||
def instantiate_prompt(node_type, class_object: Type[BasePromptTemplate], params: Dict):
|
||||
if node_type == "ZeroShotPrompt":
|
||||
if "tools" not in params:
|
||||
params["tools"] = []
|
||||
|
|
@ -116,7 +111,7 @@ def instantiate_prompt(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_tool(node_type, class_object, params):
|
||||
def instantiate_tool(node_type, class_object: Type[BaseTool], params: Dict):
|
||||
if node_type == "JsonSpec":
|
||||
params["dict_"] = load_file_into_dict(params.pop("path"))
|
||||
return class_object(**params)
|
||||
|
|
@ -134,7 +129,7 @@ def instantiate_tool(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_toolkit(node_type, class_object, params):
|
||||
def instantiate_toolkit(node_type, class_object: Type[BaseToolkit], params: Dict):
|
||||
loaded_toolkit = class_object(**params)
|
||||
# Commenting this out for now to use toolkits as normal tools
|
||||
# if toolkits_creator.has_create_function(node_type):
|
||||
|
|
@ -144,7 +139,7 @@ def instantiate_toolkit(node_type, class_object, params):
|
|||
return loaded_toolkit
|
||||
|
||||
|
||||
def instantiate_embedding(class_object, params):
|
||||
def instantiate_embedding(class_object, params: Dict):
|
||||
params.pop("model", None)
|
||||
params.pop("headers", None)
|
||||
try:
|
||||
|
|
@ -158,40 +153,81 @@ def instantiate_embedding(class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_vectorstore(class_object, params):
|
||||
if len(params.get("documents", [])) == 0:
|
||||
raise ValueError(
|
||||
"The source you provided did not load correctly or was empty."
|
||||
"This may cause an error in the vectorstore."
|
||||
def instantiate_vectorstore(class_object: Type[VectorStore], params: Dict):
|
||||
search_kwargs = params.pop("search_kwargs", {})
|
||||
if initializer := vecstore_initializer.get(class_object.__name__):
|
||||
vecstore = initializer(class_object, params)
|
||||
else:
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
vecstore = class_object.from_documents(**params)
|
||||
|
||||
# ! This might not work. Need to test
|
||||
if search_kwargs and hasattr(vecstore, "as_retriever"):
|
||||
vecstore = vecstore.as_retriever(search_kwargs=search_kwargs)
|
||||
|
||||
return vecstore
|
||||
|
||||
|
||||
def instantiate_documentloader(class_object: Type[BaseLoader], params: Dict):
|
||||
if "file_filter" in params:
|
||||
# file_filter will be a string but we need a function
|
||||
# that will be used to filter the files using file_filter
|
||||
# like lambda x: x.endswith(".txt") but as we don't know
|
||||
# anything besides the string, we will simply check if the string is
|
||||
# in x and if it is, we will return True
|
||||
file_filter = params.pop("file_filter", None)
|
||||
extensions = file_filter.split(",")
|
||||
params["file_filter"] = lambda x: any(
|
||||
extension.strip() in x for extension in extensions
|
||||
)
|
||||
# Chroma requires all metadata values to not be None
|
||||
if class_object.__name__ == "Chroma":
|
||||
for doc in params["documents"]:
|
||||
if doc.metadata is None:
|
||||
doc.metadata = {}
|
||||
for key, value in doc.metadata.items():
|
||||
if value is None:
|
||||
doc.metadata[key] = ""
|
||||
return class_object.from_documents(**params)
|
||||
metadata = params.pop("metadata", None)
|
||||
if metadata and isinstance(metadata, str):
|
||||
try:
|
||||
metadata = json.loads(metadata)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError(
|
||||
"The metadata you provided is not a valid JSON string."
|
||||
) from exc
|
||||
docs = class_object(**params).load()
|
||||
# Now if metadata is an empty dict, we will not add it to the documents
|
||||
if metadata:
|
||||
for doc in docs:
|
||||
# If the document already has metadata, we will not overwrite it
|
||||
if not doc.metadata:
|
||||
doc.metadata = metadata
|
||||
else:
|
||||
doc.metadata.update(metadata)
|
||||
|
||||
return docs
|
||||
|
||||
|
||||
def instantiate_documentloader(class_object, params):
|
||||
return class_object(**params).load()
|
||||
|
||||
|
||||
def instantiate_textsplitter(class_object, params):
|
||||
def instantiate_textsplitter(
|
||||
class_object,
|
||||
params: Dict,
|
||||
):
|
||||
try:
|
||||
documents = params.pop("documents")
|
||||
except KeyError as e:
|
||||
except KeyError as exc:
|
||||
raise ValueError(
|
||||
"The source you provided did not load correctly or was empty."
|
||||
"Try changing the chunk_size of the Text Splitter."
|
||||
) from e
|
||||
text_splitter = class_object(**params)
|
||||
) from exc
|
||||
|
||||
if (
|
||||
"separator_type" in params
|
||||
and params["separator_type"] == "Text"
|
||||
or "separator_type" not in params
|
||||
):
|
||||
text_splitter = class_object(**params)
|
||||
else:
|
||||
params["language"] = params.pop("separator_type", None)
|
||||
params.pop("separators", None)
|
||||
text_splitter = class_object.from_language(**params)
|
||||
return text_splitter.split_documents(documents)
|
||||
|
||||
|
||||
def instantiate_utility(node_type, class_object, params):
|
||||
def instantiate_utility(node_type, class_object, params: Dict):
|
||||
if node_type == "SQLDatabase":
|
||||
return class_object.from_uri(params.pop("uri"))
|
||||
return class_object(**params)
|
||||
|
|
@ -213,48 +249,14 @@ def replace_zero_shot_prompt_with_prompt_template(nodes):
|
|||
return nodes
|
||||
|
||||
|
||||
def load_langchain_type_from_config(config: Dict[str, Any]):
|
||||
"""Load langchain type from config"""
|
||||
# Get type list
|
||||
type_list = get_type_list()
|
||||
if config["_type"] in type_list["agents"]:
|
||||
config = util.update_verbose(config, new_value=False)
|
||||
return load_agent_executor_from_config(config, verbose=True)
|
||||
elif config["_type"] in type_list["chains"]:
|
||||
config = util.update_verbose(config, new_value=False)
|
||||
return load_chain_from_config(config, verbose=True)
|
||||
elif config["_type"] in type_list["llms"]:
|
||||
config = util.update_verbose(config, new_value=True)
|
||||
return load_llm_from_config(config)
|
||||
else:
|
||||
raise ValueError("Type should be either agent, chain or llm")
|
||||
|
||||
|
||||
def load_agent_executor_from_config(
|
||||
config: dict,
|
||||
llm: Optional[BaseLanguageModel] = None,
|
||||
tools: Optional[list[Tool]] = None,
|
||||
callback_manager: Optional[BaseCallbackManager] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
tools = load_tools_from_config(config["allowed_tools"])
|
||||
config["allowed_tools"] = [tool.name for tool in tools] if tools else []
|
||||
agent_obj = load_agent_from_config(config, llm, tools, **kwargs)
|
||||
|
||||
return AgentExecutor.from_agent_and_tools(
|
||||
agent=agent_obj,
|
||||
tools=tools,
|
||||
callback_manager=callback_manager,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def load_agent_executor(agent_class: type[agent_module.Agent], params, **kwargs):
|
||||
"""Load agent executor from agent class, tools and chain"""
|
||||
allowed_tools = params.get("allowed_tools", [])
|
||||
allowed_tools: Sequence[BaseTool] = params.get("allowed_tools", [])
|
||||
llm_chain = params["llm_chain"]
|
||||
# if allowed_tools is not a list or set, make it a list
|
||||
if not isinstance(allowed_tools, (list, set)):
|
||||
if not isinstance(allowed_tools, (list, set)) and isinstance(
|
||||
allowed_tools, BaseTool
|
||||
):
|
||||
allowed_tools = [allowed_tools]
|
||||
tool_names = [tool.name for tool in allowed_tools]
|
||||
# Agent class requires an output_parser but Agent classes
|
||||
|
|
@ -273,46 +275,6 @@ def load_toolkits_executor(node_type: str, toolkit: BaseToolkit, params: dict):
|
|||
return create_function(llm=llm, toolkit=toolkit)
|
||||
|
||||
|
||||
def load_tools_from_config(tool_list: list[dict]) -> list:
|
||||
"""Load tools based on a config list.
|
||||
|
||||
Args:
|
||||
config: config list.
|
||||
|
||||
Returns:
|
||||
List of tools.
|
||||
"""
|
||||
tools = []
|
||||
for tool in tool_list:
|
||||
tool_type = tool.pop("_type")
|
||||
llm_config = tool.pop("llm", None)
|
||||
llm = load_llm_from_config(llm_config) if llm_config else None
|
||||
kwargs = tool
|
||||
if tool_type in _BASE_TOOLS:
|
||||
tools.append(_BASE_TOOLS[tool_type]())
|
||||
elif tool_type in _LLM_TOOLS:
|
||||
if llm is None:
|
||||
raise ValueError(f"Tool {tool_type} requires an LLM to be provided")
|
||||
tools.append(_LLM_TOOLS[tool_type](llm))
|
||||
elif tool_type in _EXTRA_LLM_TOOLS:
|
||||
if llm is None:
|
||||
raise ValueError(f"Tool {tool_type} requires an LLM to be provided")
|
||||
_get_llm_tool_func, extra_keys = _EXTRA_LLM_TOOLS[tool_type]
|
||||
if missing_keys := set(extra_keys).difference(kwargs):
|
||||
raise ValueError(
|
||||
f"Tool {tool_type} requires some parameters that were not "
|
||||
f"provided: {missing_keys}"
|
||||
)
|
||||
tools.append(_get_llm_tool_func(llm=llm, **kwargs))
|
||||
elif tool_type in _EXTRA_OPTIONAL_TOOLS:
|
||||
_get_tool_func, extra_keys = _EXTRA_OPTIONAL_TOOLS[tool_type]
|
||||
kwargs = {k: value for k, value in kwargs.items() if value}
|
||||
tools.append(_get_tool_func(**kwargs))
|
||||
else:
|
||||
raise ValueError(f"Got unknown tool {tool_type}")
|
||||
return tools
|
||||
|
||||
|
||||
def build_prompt_template(prompt, tools):
|
||||
"""Build PromptTemplate from ZeroShotPrompt"""
|
||||
prefix = prompt["node"]["template"]["prefix"]["value"]
|
||||
223
src/backend/langflow/interface/initialize/vector_store.py
Normal file
223
src/backend/langflow/interface/initialize/vector_store.py
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
import json
|
||||
from typing import Any, Callable, Dict, Type
|
||||
from langchain.vectorstores import (
|
||||
Pinecone,
|
||||
Qdrant,
|
||||
Chroma,
|
||||
FAISS,
|
||||
Weaviate,
|
||||
SupabaseVectorStore,
|
||||
MongoDBAtlasVectorSearch,
|
||||
)
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def docs_in_params(params: dict) -> bool:
|
||||
"""Check if params has documents OR texts and one of them is not an empty list,
|
||||
If any of them is not an empty list, return True, else return False"""
|
||||
return ("documents" in params and params["documents"]) or (
|
||||
"texts" in params and params["texts"]
|
||||
)
|
||||
|
||||
|
||||
def initialize_mongodb(class_object: Type[MongoDBAtlasVectorSearch], params: dict):
|
||||
"""Initialize mongodb and return the class object"""
|
||||
|
||||
MONGODB_ATLAS_CLUSTER_URI = params.pop("mongodb_atlas_cluster_uri")
|
||||
if not MONGODB_ATLAS_CLUSTER_URI:
|
||||
raise ValueError("Mongodb atlas cluster uri must be provided in the params")
|
||||
from pymongo import MongoClient
|
||||
import certifi
|
||||
|
||||
client: MongoClient = MongoClient(
|
||||
MONGODB_ATLAS_CLUSTER_URI, tlsCAFile=certifi.where()
|
||||
)
|
||||
db_name = params.pop("db_name", None)
|
||||
collection_name = params.pop("collection_name", None)
|
||||
if not db_name or not collection_name:
|
||||
raise ValueError("db_name and collection_name must be provided in the params")
|
||||
|
||||
index_name = params.pop("index_name", None)
|
||||
if not index_name:
|
||||
raise ValueError("index_name must be provided in the params")
|
||||
|
||||
collection = client[db_name][collection_name]
|
||||
if not docs_in_params(params):
|
||||
# __init__ requires collection, embedding and index_name
|
||||
init_args = {
|
||||
"collection": collection,
|
||||
"index_name": index_name,
|
||||
"embedding": params.get("embedding"),
|
||||
}
|
||||
|
||||
return class_object(**init_args)
|
||||
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
|
||||
params["collection"] = collection
|
||||
params["index_name"] = index_name
|
||||
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
def initialize_supabase(class_object: Type[SupabaseVectorStore], params: dict):
|
||||
"""Initialize supabase and return the class object"""
|
||||
from supabase.client import Client, create_client
|
||||
|
||||
if "supabase_url" not in params or "supabase_service_key" not in params:
|
||||
raise ValueError("Supabase url and service key must be provided in the params")
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
|
||||
client_kwargs = {
|
||||
"supabase_url": params.pop("supabase_url"),
|
||||
"supabase_key": params.pop("supabase_service_key"),
|
||||
}
|
||||
|
||||
supabase: Client = create_client(**client_kwargs)
|
||||
if not docs_in_params(params):
|
||||
params.pop("documents", None)
|
||||
params.pop("texts", None)
|
||||
return class_object(client=supabase, **params)
|
||||
# If there are docs in the params, create a new index
|
||||
|
||||
return class_object.from_documents(client=supabase, **params)
|
||||
|
||||
|
||||
def initialize_weaviate(class_object: Type[Weaviate], params: dict):
|
||||
"""Initialize weaviate and return the class object"""
|
||||
if not docs_in_params(params):
|
||||
import weaviate # type: ignore
|
||||
|
||||
client_kwargs_json = params.get("client_kwargs", "{}")
|
||||
client_kwargs = json.loads(client_kwargs_json)
|
||||
client_params = {
|
||||
"url": params.get("weaviate_url"),
|
||||
}
|
||||
client_params.update(client_kwargs)
|
||||
weaviate_client = weaviate.Client(**client_params)
|
||||
|
||||
new_params = {
|
||||
"client": weaviate_client,
|
||||
"index_name": params.get("index_name"),
|
||||
"text_key": params.get("text_key"),
|
||||
}
|
||||
return class_object(**new_params)
|
||||
# If there are docs in the params, create a new index
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
def initialize_faiss(class_object: Type[FAISS], params: dict):
|
||||
"""Initialize faiss and return the class object"""
|
||||
|
||||
if not docs_in_params(params):
|
||||
return class_object.load_local
|
||||
|
||||
save_local = params.get("save_local")
|
||||
faiss_index = class_object(**params)
|
||||
if save_local:
|
||||
faiss_index.save_local(folder_path=save_local)
|
||||
return faiss_index
|
||||
|
||||
|
||||
def initialize_pinecone(class_object: Type[Pinecone], params: dict):
|
||||
"""Initialize pinecone and return the class object"""
|
||||
|
||||
import pinecone # type: ignore
|
||||
|
||||
pinecone_api_key = params.get("pinecone_api_key")
|
||||
pinecone_env = params.get("pinecone_env")
|
||||
|
||||
if pinecone_api_key is None or pinecone_env is None:
|
||||
if os.getenv("PINECONE_API_KEY") is not None:
|
||||
pinecone_api_key = os.getenv("PINECONE_API_KEY")
|
||||
if os.getenv("PINECONE_ENV") is not None:
|
||||
pinecone_env = os.getenv("PINECONE_ENV")
|
||||
|
||||
if pinecone_api_key is None or pinecone_env is None:
|
||||
raise ValueError(
|
||||
"Pinecone API key and environment must be provided in the params"
|
||||
)
|
||||
|
||||
# initialize pinecone
|
||||
pinecone.init(
|
||||
api_key=pinecone_api_key, # find at app.pinecone.io
|
||||
environment=pinecone_env, # next to api key in console
|
||||
)
|
||||
|
||||
# If there are no docs in the params, return an existing index
|
||||
# but first remove any texts or docs keys from the params
|
||||
if not docs_in_params(params):
|
||||
existing_index_params = {
|
||||
"embedding": params.pop("embedding"),
|
||||
}
|
||||
if "index_name" in params:
|
||||
existing_index_params["index_name"] = params.pop("index_name")
|
||||
if "namespace" in params:
|
||||
existing_index_params["namespace"] = params.pop("namespace")
|
||||
|
||||
return class_object.from_existing_index(**existing_index_params)
|
||||
# If there are docs in the params, create a new index
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
def initialize_chroma(class_object: Type[Chroma], params: dict):
|
||||
"""Initialize a ChromaDB object from the params"""
|
||||
persist = params.pop("persist", False)
|
||||
if not docs_in_params(params):
|
||||
params.pop("documents", None)
|
||||
params.pop("texts", None)
|
||||
params["embedding_function"] = params.pop("embedding")
|
||||
chromadb = class_object(**params)
|
||||
else:
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
for doc in params["documents"]:
|
||||
if doc.metadata is None:
|
||||
doc.metadata = {}
|
||||
for key, value in doc.metadata.items():
|
||||
if value is None:
|
||||
doc.metadata[key] = ""
|
||||
chromadb = class_object.from_documents(**params)
|
||||
if persist:
|
||||
chromadb.persist()
|
||||
return chromadb
|
||||
|
||||
|
||||
def initialize_qdrant(class_object: Type[Qdrant], params: dict):
|
||||
if not docs_in_params(params):
|
||||
if "location" not in params and "api_key" not in params:
|
||||
raise ValueError("Location and API key must be provided in the params")
|
||||
from qdrant_client import QdrantClient
|
||||
|
||||
client_params = {
|
||||
"location": params.pop("location"),
|
||||
"api_key": params.pop("api_key"),
|
||||
}
|
||||
lc_params = {
|
||||
"collection_name": params.pop("collection_name"),
|
||||
"embeddings": params.pop("embedding"),
|
||||
}
|
||||
client = QdrantClient(**client_params)
|
||||
|
||||
return class_object(client=client, **lc_params)
|
||||
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
vecstore_initializer: Dict[str, Callable[[Type[Any], dict], Any]] = {
|
||||
"Pinecone": initialize_pinecone,
|
||||
"Chroma": initialize_chroma,
|
||||
"Qdrant": initialize_qdrant,
|
||||
"Weaviate": initialize_weaviate,
|
||||
"FAISS": initialize_faiss,
|
||||
"SupabaseVectorStore": initialize_supabase,
|
||||
"MongoDBAtlasVectorSearch": initialize_mongodb,
|
||||
}
|
||||
|
|
@ -4,9 +4,12 @@ import os
|
|||
from io import BytesIO
|
||||
import re
|
||||
|
||||
|
||||
import yaml
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from PIL.Image import Image
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.chat.config import ChatConfig
|
||||
|
||||
|
||||
def load_file_into_dict(file_path: str) -> dict:
|
||||
|
|
@ -48,9 +51,9 @@ def try_setting_streaming_options(langchain_object, websocket):
|
|||
|
||||
if isinstance(llm, BaseLanguageModel):
|
||||
if hasattr(llm, "streaming") and isinstance(llm.streaming, bool):
|
||||
llm.streaming = True
|
||||
llm.streaming = ChatConfig.streaming
|
||||
elif hasattr(llm, "stream") and isinstance(llm.stream, bool):
|
||||
llm.stream = True
|
||||
llm.stream = ChatConfig.streaming
|
||||
|
||||
return langchain_object
|
||||
|
||||
|
|
@ -58,3 +61,22 @@ def try_setting_streaming_options(langchain_object, websocket):
|
|||
def extract_input_variables_from_prompt(prompt: str) -> list[str]:
|
||||
"""Extract input variables from prompt."""
|
||||
return re.findall(r"{(.*?)}", prompt)
|
||||
|
||||
|
||||
def setup_llm_caching():
|
||||
"""Setup LLM caching."""
|
||||
|
||||
try:
|
||||
import langchain
|
||||
from langflow.settings import settings
|
||||
from langflow.interface.importing.utils import import_class
|
||||
|
||||
cache_class = import_class(f"langchain.cache.{settings.cache}")
|
||||
|
||||
logger.debug(f"Setting up LLM caching with {cache_class.__name__}")
|
||||
langchain.llm_cache = cache_class()
|
||||
logger.info(f"LLM caching setup with {cache_class.__name__}")
|
||||
except ImportError:
|
||||
logger.warning(f"Could not import {settings.cache}. ")
|
||||
except Exception as exc:
|
||||
logger.warning(f"Could not setup LLM caching. Error: {exc}")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from langflow.api import router
|
||||
from langflow.database.base import create_db_and_tables
|
||||
from langflow.interface.utils import setup_llm_caching
|
||||
|
||||
|
||||
def create_app():
|
||||
|
|
@ -28,6 +33,43 @@ def create_app():
|
|||
|
||||
app.include_router(router)
|
||||
app.on_event("startup")(create_db_and_tables)
|
||||
app.on_event("startup")(setup_llm_caching)
|
||||
return app
|
||||
|
||||
|
||||
def setup_static_files(app: FastAPI, static_files_dir: Path):
|
||||
"""
|
||||
Setup the static files directory.
|
||||
Args:
|
||||
app (FastAPI): FastAPI app.
|
||||
path (str): Path to the static files directory.
|
||||
"""
|
||||
app.mount(
|
||||
"/",
|
||||
StaticFiles(directory=static_files_dir, html=True),
|
||||
name="static",
|
||||
)
|
||||
|
||||
@app.exception_handler(404)
|
||||
async def custom_404_handler(request, __):
|
||||
path = static_files_dir / "index.html"
|
||||
|
||||
if not path.exists():
|
||||
raise RuntimeError(f"File at path {path} does not exist.")
|
||||
return FileResponse(path)
|
||||
|
||||
|
||||
# app = create_app()
|
||||
# setup_static_files(app, static_files_dir)
|
||||
def setup_app(static_files_dir: Optional[Path]) -> FastAPI:
|
||||
"""Setup the FastAPI app."""
|
||||
# get the directory of the current file
|
||||
if not static_files_dir:
|
||||
frontend_path = Path(__file__).parent
|
||||
static_files_dir = frontend_path / "frontend"
|
||||
|
||||
app = create_app()
|
||||
setup_static_files(app, static_files_dir)
|
||||
return app
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import contextlib
|
||||
import io
|
||||
from pathlib import Path
|
||||
from langchain.schema import AgentAction
|
||||
import json
|
||||
|
|
@ -10,7 +8,8 @@ from langflow.interface.run import (
|
|||
)
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.graph import Graph
|
||||
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
|
||||
|
|
@ -55,69 +54,42 @@ def format_actions(actions: List[Tuple[AgentAction, str]]) -> str:
|
|||
return "\n".join(output)
|
||||
|
||||
|
||||
def get_result_and_thought(langchain_object, message: str):
|
||||
def get_result_and_thought(langchain_object: Any, inputs: dict):
|
||||
"""Get result and thought from extracted json"""
|
||||
try:
|
||||
if hasattr(langchain_object, "verbose"):
|
||||
langchain_object.verbose = True
|
||||
chat_input = None
|
||||
memory_key = ""
|
||||
if hasattr(langchain_object, "memory") and langchain_object.memory is not None:
|
||||
memory_key = langchain_object.memory.memory_key
|
||||
|
||||
if hasattr(langchain_object, "input_keys"):
|
||||
for key in langchain_object.input_keys:
|
||||
if key not in [memory_key, "chat_history"]:
|
||||
chat_input = {key: message}
|
||||
else:
|
||||
chat_input = message # type: ignore
|
||||
|
||||
if hasattr(langchain_object, "return_intermediate_steps"):
|
||||
# https://github.com/hwchase17/langchain/issues/2068
|
||||
# Deactivating until we have a frontend solution
|
||||
# to display intermediate steps
|
||||
langchain_object.return_intermediate_steps = False
|
||||
langchain_object.return_intermediate_steps = True
|
||||
|
||||
fix_memory_inputs(langchain_object)
|
||||
|
||||
with io.StringIO() as output_buffer, contextlib.redirect_stdout(output_buffer):
|
||||
try:
|
||||
# if hasattr(langchain_object, "acall"):
|
||||
# output = await langchain_object.acall(chat_input)
|
||||
# else:
|
||||
output = langchain_object(chat_input)
|
||||
except ValueError as exc:
|
||||
# make the error message more informative
|
||||
logger.debug(f"Error: {str(exc)}")
|
||||
output = langchain_object.run(chat_input)
|
||||
|
||||
intermediate_steps = (
|
||||
output.get("intermediate_steps", []) if isinstance(output, dict) else []
|
||||
)
|
||||
|
||||
result = (
|
||||
output.get(langchain_object.output_keys[0])
|
||||
if isinstance(output, dict)
|
||||
else output
|
||||
)
|
||||
if intermediate_steps:
|
||||
thought = format_actions(intermediate_steps)
|
||||
else:
|
||||
thought = output_buffer.getvalue()
|
||||
try:
|
||||
output = langchain_object(inputs, return_only_outputs=True)
|
||||
except ValueError as exc:
|
||||
# make the error message more informative
|
||||
logger.debug(f"Error: {str(exc)}")
|
||||
output = langchain_object.run(inputs)
|
||||
|
||||
except Exception as exc:
|
||||
raise ValueError(f"Error: {str(exc)}") from exc
|
||||
return result, thought
|
||||
return output
|
||||
|
||||
|
||||
def process_graph_cached(data_graph: Dict[str, Any], message: str):
|
||||
def get_input_str_if_only_one_input(inputs: dict) -> Optional[str]:
|
||||
"""Get input string if only one input is provided"""
|
||||
return list(inputs.values())[0] if len(inputs) == 1 else None
|
||||
|
||||
|
||||
def process_graph_cached(data_graph: Dict[str, Any], inputs: Optional[dict] = None):
|
||||
"""
|
||||
Process graph by extracting input variables and replacing ZeroShotPrompt
|
||||
with PromptTemplate,then run the graph and return the result and thought.
|
||||
"""
|
||||
# Load langchain object
|
||||
langchain_object = build_langchain_object_with_caching(data_graph)
|
||||
logger.debug("Loaded langchain object")
|
||||
logger.debug("Loaded LangChain object")
|
||||
|
||||
if langchain_object is None:
|
||||
# Raise user facing error
|
||||
|
|
@ -126,10 +98,16 @@ def process_graph_cached(data_graph: Dict[str, Any], message: str):
|
|||
)
|
||||
|
||||
# Generate result and thought
|
||||
logger.debug("Generating result and thought")
|
||||
result, thought = get_result_and_thought(langchain_object, message)
|
||||
logger.debug("Generated result and thought")
|
||||
return {"result": str(result), "thought": thought.strip()}
|
||||
if isinstance(langchain_object, Chain):
|
||||
if inputs is None:
|
||||
raise ValueError("Inputs must be provided for a Chain")
|
||||
logger.debug("Generating result and thought")
|
||||
result = get_result_and_thought(langchain_object, inputs)
|
||||
logger.debug("Generated result and thought")
|
||||
elif isinstance(langchain_object, VectorStore):
|
||||
class_name = langchain_object.__class__.__name__
|
||||
result = {"message": f"Processed {class_name} successfully"}
|
||||
return result
|
||||
|
||||
|
||||
def load_flow_from_json(
|
||||
|
|
@ -206,7 +184,8 @@ def apply_tweaks(node: Dict[str, Any], node_tweaks: Dict[str, Any]) -> None:
|
|||
|
||||
for tweak_name, tweak_value in node_tweaks.items():
|
||||
if tweak_name and tweak_value and tweak_name in template_data:
|
||||
template_data[tweak_name]["value"] = tweak_value
|
||||
key = tweak_name if tweak_name == "file_path" else "value"
|
||||
template_data[tweak_name][key] = tweak_value
|
||||
|
||||
|
||||
def process_tweaks(
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
import os
|
||||
from typing import List
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseSettings, root_validator
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
chains: List[str] = []
|
||||
agents: List[str] = []
|
||||
prompts: List[str] = []
|
||||
llms: List[str] = []
|
||||
tools: List[str] = []
|
||||
memories: List[str] = []
|
||||
embeddings: List[str] = []
|
||||
vectorstores: List[str] = []
|
||||
documentloaders: List[str] = []
|
||||
wrappers: List[str] = []
|
||||
toolkits: List[str] = []
|
||||
textsplitters: List[str] = []
|
||||
utilities: List[str] = []
|
||||
chains: dict = {}
|
||||
agents: dict = {}
|
||||
prompts: dict = {}
|
||||
llms: dict = {}
|
||||
tools: dict = {}
|
||||
memories: dict = {}
|
||||
embeddings: dict = {}
|
||||
vectorstores: dict = {}
|
||||
documentloaders: dict = {}
|
||||
wrappers: dict = {}
|
||||
toolkits: dict = {}
|
||||
textsplitters: dict = {}
|
||||
utilities: dict = {}
|
||||
dev: bool = False
|
||||
database_url: str = "sqlite:///./langflow.db"
|
||||
cache: str = "InMemoryCache"
|
||||
remove_api_keys: bool = False
|
||||
|
||||
class Config:
|
||||
|
|
@ -37,16 +37,16 @@ class Settings(BaseSettings):
|
|||
|
||||
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.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.dev = dev
|
||||
|
||||
def update_settings(self, **kwargs):
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@ class TemplateFieldCreator(BaseModel, ABC):
|
|||
suffixes: list[str] = []
|
||||
fileTypes: list[str] = []
|
||||
file_types: list[str] = []
|
||||
content: Union[str, None] = None
|
||||
file_path: Union[str, None] = None
|
||||
password: bool = False
|
||||
options: list[str] = []
|
||||
name: str = ""
|
||||
display_name: Optional[str] = None
|
||||
advanced: bool = False
|
||||
info: Optional[str] = ""
|
||||
|
||||
def to_dict(self):
|
||||
result = self.dict()
|
||||
|
|
@ -35,7 +36,7 @@ class TemplateFieldCreator(BaseModel, ABC):
|
|||
result["fileTypes"] = result.pop("file_types")
|
||||
|
||||
if self.field_type == "file":
|
||||
result["content"] = self.content
|
||||
result["file_path"] = self.file_path
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,21 @@ class FrontendNode(BaseModel):
|
|||
base_classes: List[str]
|
||||
name: str = ""
|
||||
display_name: str = ""
|
||||
documentation: str = ""
|
||||
|
||||
def set_documentation(self, documentation: str) -> None:
|
||||
"""Sets the documentation of the frontend node."""
|
||||
self.documentation = documentation
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Returns a dict representation of the frontend node."""
|
||||
return {
|
||||
self.name: {
|
||||
"template": self.template.to_dict(self.format_field),
|
||||
"description": self.description,
|
||||
"base_classes": self.base_classes,
|
||||
"display_name": self.display_name or self.name,
|
||||
"documentation": self.documentation,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,37 @@ class ChainFrontendNode(FrontendNode):
|
|||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="BaseChatMemory",
|
||||
required=False,
|
||||
required=True,
|
||||
show=True,
|
||||
name="memory",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
# add return_source_documents
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="bool",
|
||||
required=False,
|
||||
show=True,
|
||||
name="return_source_documents",
|
||||
advanced=False,
|
||||
value=True,
|
||||
display_name="Return source documents",
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
is_list=True,
|
||||
show=True,
|
||||
multiline=False,
|
||||
options=QA_CHAIN_TYPES,
|
||||
value=QA_CHAIN_TYPES[0],
|
||||
name="chain_type",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
|
|
@ -47,7 +72,7 @@ class ChainFrontendNode(FrontendNode):
|
|||
field.show = True
|
||||
field.advanced = False
|
||||
if field.name == "memory":
|
||||
field.required = False
|
||||
# field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
if field.name == "verbose":
|
||||
|
|
@ -59,6 +84,12 @@ class ChainFrontendNode(FrontendNode):
|
|||
field.show = True
|
||||
field.advanced = False
|
||||
|
||||
if field.name == "return_source_documents":
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = True
|
||||
field.value = True
|
||||
|
||||
|
||||
class SeriesCharacterChainNode(FrontendNode):
|
||||
name: str = "SeriesCharacterChain"
|
||||
|
|
|
|||
|
|
@ -49,3 +49,12 @@ CTRANSFORMERS_DEFAULT_CONFIG = {
|
|||
"context_length": -1,
|
||||
"gpu_layers": 0,
|
||||
}
|
||||
|
||||
# This variable is used to tell the user
|
||||
# that it can be changed to use other APIs
|
||||
# like Prem and LocalAI
|
||||
OPENAI_API_BASE_INFO = """
|
||||
The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.
|
||||
|
||||
You can change this to use other APIs like JinaChat, LocalAI and Prem.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Optional
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
|
||||
|
||||
def build_template(
|
||||
def build_file_field(
|
||||
suffixes: list, fileTypes: list, name: str = "file_path"
|
||||
) -> TemplateField:
|
||||
"""Build a template field for a document loader."""
|
||||
|
|
@ -19,33 +20,35 @@ def build_template(
|
|||
|
||||
class DocumentLoaderFrontNode(FrontendNode):
|
||||
file_path_templates = {
|
||||
"AirbyteJSONLoader": build_template(suffixes=[".json"], fileTypes=["json"]),
|
||||
"CoNLLULoader": build_template(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"CSVLoader": build_template(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"UnstructuredEmailLoader": build_template(suffixes=[".eml"], fileTypes=["eml"]),
|
||||
"EverNoteLoader": build_template(suffixes=[".xml"], fileTypes=["xml"]),
|
||||
"FacebookChatLoader": build_template(suffixes=[".json"], fileTypes=["json"]),
|
||||
"GutenbergLoader": build_template(suffixes=[".txt"], fileTypes=["txt"]),
|
||||
"BSHTMLLoader": build_template(suffixes=[".html"], fileTypes=["html"]),
|
||||
"UnstructuredHTMLLoader": build_template(
|
||||
"AirbyteJSONLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]),
|
||||
"CoNLLULoader": build_file_field(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"CSVLoader": build_file_field(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"UnstructuredEmailLoader": build_file_field(
|
||||
suffixes=[".eml"], fileTypes=["eml"]
|
||||
),
|
||||
"SlackDirectoryLoader": build_file_field(suffixes=[".zip"], fileTypes=["zip"]),
|
||||
"EverNoteLoader": build_file_field(suffixes=[".xml"], fileTypes=["xml"]),
|
||||
"FacebookChatLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]),
|
||||
"GutenbergLoader": build_file_field(suffixes=[".txt"], fileTypes=["txt"]),
|
||||
"BSHTMLLoader": build_file_field(suffixes=[".html"], fileTypes=["html"]),
|
||||
"UnstructuredHTMLLoader": build_file_field(
|
||||
suffixes=[".html"], fileTypes=["html"]
|
||||
),
|
||||
"UnstructuredImageLoader": build_template(
|
||||
"UnstructuredImageLoader": build_file_field(
|
||||
suffixes=[".jpg", ".jpeg", ".png", ".gif", ".bmp"],
|
||||
fileTypes=["jpg", "jpeg", "png", "gif", "bmp"],
|
||||
),
|
||||
"UnstructuredMarkdownLoader": build_template(
|
||||
"UnstructuredMarkdownLoader": build_file_field(
|
||||
suffixes=[".md"], fileTypes=["md"]
|
||||
),
|
||||
"PyPDFLoader": build_template(suffixes=[".pdf"], fileTypes=["pdf"]),
|
||||
"UnstructuredPowerPointLoader": build_template(
|
||||
"PyPDFLoader": build_file_field(suffixes=[".pdf"], fileTypes=["pdf"]),
|
||||
"UnstructuredPowerPointLoader": build_file_field(
|
||||
suffixes=[".pptx", ".ppt"], fileTypes=["pptx", "ppt"]
|
||||
),
|
||||
"SlackDirectoryLoader": build_template(suffixes=[".zip"], fileTypes=["zip"]),
|
||||
"SRTLoader": build_template(suffixes=[".srt"], fileTypes=["srt"]),
|
||||
"TelegramChatLoader": build_template(suffixes=[".json"], fileTypes=["json"]),
|
||||
"TextLoader": build_template(suffixes=[".txt"], fileTypes=["txt"]),
|
||||
"UnstructuredWordDocumentLoader": build_template(
|
||||
"SRTLoader": build_file_field(suffixes=[".srt"], fileTypes=["srt"]),
|
||||
"TelegramChatLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]),
|
||||
"TextLoader": build_file_field(suffixes=[".txt"], fileTypes=["txt"]),
|
||||
"UnstructuredWordDocumentLoader": build_file_field(
|
||||
suffixes=[".docx", ".doc"], fileTypes=["docx", "doc"]
|
||||
),
|
||||
}
|
||||
|
|
@ -53,7 +56,54 @@ class DocumentLoaderFrontNode(FrontendNode):
|
|||
def add_extra_fields(self) -> None:
|
||||
name = None
|
||||
display_name = "Web Page"
|
||||
if self.template.type_name in self.file_path_templates:
|
||||
if self.template.type_name in {"GitLoader"}:
|
||||
# Add fields repo_path, clone_url, branch and file_filter
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
show=True,
|
||||
name="repo_path",
|
||||
value="",
|
||||
display_name="Path to repository",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=False,
|
||||
show=True,
|
||||
name="clone_url",
|
||||
value="",
|
||||
display_name="Clone URL",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
show=True,
|
||||
name="branch",
|
||||
value="",
|
||||
display_name="Branch",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=False,
|
||||
show=True,
|
||||
name="file_filter",
|
||||
value="",
|
||||
display_name="File extensions (comma-separated)",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
|
||||
elif self.template.type_name in self.file_path_templates:
|
||||
self.template.add_field(self.file_path_templates[self.template.type_name])
|
||||
elif self.template.type_name in {
|
||||
"WebBaseLoader",
|
||||
|
|
@ -67,8 +117,9 @@ class DocumentLoaderFrontNode(FrontendNode):
|
|||
elif self.template.type_name in {"GitbookLoader"}:
|
||||
name = "web_page"
|
||||
elif self.template.type_name in {
|
||||
"NotionDirectoryLoader",
|
||||
"DirectoryLoader",
|
||||
"ReadTheDocsLoader",
|
||||
"NotionDirectoryLoader",
|
||||
}:
|
||||
name = "path"
|
||||
display_name = "Local directory"
|
||||
|
|
@ -83,3 +134,34 @@ class DocumentLoaderFrontNode(FrontendNode):
|
|||
display_name=display_name,
|
||||
)
|
||||
)
|
||||
if self.template.type_name in {"DirectoryLoader"}:
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
show=True,
|
||||
name="glob",
|
||||
value="**/*.txt",
|
||||
display_name="glob",
|
||||
)
|
||||
)
|
||||
# add a metadata field of type dict
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="code",
|
||||
required=True,
|
||||
show=True,
|
||||
name="metadata",
|
||||
value="{}",
|
||||
display_name="Metadata",
|
||||
multiline=False,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
if field.name == "metadata":
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
field.show = True
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from typing import Optional
|
|||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.frontend_node.constants import CTRANSFORMERS_DEFAULT_CONFIG
|
||||
from langflow.template.frontend_node.constants import OPENAI_API_BASE_INFO
|
||||
|
||||
|
||||
class LLMFrontendNode(FrontendNode):
|
||||
|
|
@ -17,6 +18,9 @@ class LLMFrontendNode(FrontendNode):
|
|||
if "key" not in field.name.lower() and "token" not in field.name.lower():
|
||||
field.password = False
|
||||
|
||||
if field.name == "openai_api_base":
|
||||
field.info = OPENAI_API_BASE_INFO
|
||||
|
||||
@staticmethod
|
||||
def format_azure_field(field: TemplateField):
|
||||
if field.name == "model_name":
|
||||
|
|
|
|||
|
|
@ -18,6 +18,27 @@ class MemoryFrontendNode(FrontendNode):
|
|||
value=False,
|
||||
)
|
||||
)
|
||||
# add input_key and output_key str fields
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=False,
|
||||
show=True,
|
||||
name="input_key",
|
||||
advanced=True,
|
||||
value="",
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=False,
|
||||
show=True,
|
||||
name="output_key",
|
||||
advanced=True,
|
||||
value="",
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
|
|
@ -36,3 +57,10 @@ class MemoryFrontendNode(FrontendNode):
|
|||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
if field.name in ["input_key", "output_key"]:
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
field.value = ""
|
||||
if field.name == "memory_key":
|
||||
field.value = "chat_history"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langchain.text_splitter import Language
|
||||
|
||||
|
||||
class TextSplittersFrontendNode(FrontendNode):
|
||||
|
|
@ -17,6 +18,22 @@ class TextSplittersFrontendNode(FrontendNode):
|
|||
name = "separator"
|
||||
elif self.template.type_name == "RecursiveCharacterTextSplitter":
|
||||
name = "separators"
|
||||
# Add a field for type of separator
|
||||
# which will have Text or any value from the
|
||||
# Language enum
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
show=True,
|
||||
name="separator_type",
|
||||
advanced=False,
|
||||
is_list=True,
|
||||
options=[x.value for x in Language],
|
||||
value="Text",
|
||||
display_name="Separator Type",
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
|
|
|
|||
|
|
@ -96,6 +96,16 @@ class PythonFunctionToolNode(FrontendNode):
|
|||
name="code",
|
||||
advanced=False,
|
||||
),
|
||||
TemplateField(
|
||||
field_type="bool",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
multiline=False,
|
||||
value=False,
|
||||
name="return_direct",
|
||||
),
|
||||
],
|
||||
)
|
||||
description: str = "Python function to be executed."
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
|
|
@ -6,6 +6,19 @@ from langflow.template.frontend_node.base import FrontendNode
|
|||
|
||||
class VectorStoreFrontendNode(FrontendNode):
|
||||
def add_extra_fields(self) -> None:
|
||||
extra_fields: List[TemplateField] = []
|
||||
# Add search_kwargs field
|
||||
extra_field = TemplateField(
|
||||
name="search_kwargs",
|
||||
field_type="code",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="{}",
|
||||
)
|
||||
extra_fields.append(extra_field)
|
||||
if self.template.type_name == "Weaviate":
|
||||
extra_field = TemplateField(
|
||||
name="weaviate_url",
|
||||
|
|
@ -17,17 +30,201 @@ class VectorStoreFrontendNode(FrontendNode):
|
|||
multiline=False,
|
||||
value="http://localhost:8080",
|
||||
)
|
||||
# Add client_kwargs field
|
||||
extra_field2 = TemplateField(
|
||||
name="client_kwargs",
|
||||
field_type="code",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="{}",
|
||||
)
|
||||
extra_fields.extend((extra_field, extra_field2))
|
||||
|
||||
self.template.add_field(extra_field)
|
||||
elif self.template.type_name == "Chroma":
|
||||
# New bool field for persist parameter
|
||||
extra_field = TemplateField(
|
||||
name="persist",
|
||||
field_type="bool",
|
||||
required=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
value=True,
|
||||
display_name="Persist",
|
||||
)
|
||||
extra_fields.append(extra_field)
|
||||
elif self.template.type_name == "Pinecone":
|
||||
# add pinecone_api_key and pinecone_env
|
||||
extra_field = TemplateField(
|
||||
name="pinecone_api_key",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
)
|
||||
extra_field2 = TemplateField(
|
||||
name="pinecone_env",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
)
|
||||
extra_fields.extend((extra_field, extra_field2))
|
||||
elif self.template.type_name == "FAISS":
|
||||
extra_field = TemplateField(
|
||||
name="folder_path",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
display_name="Local Path",
|
||||
value="",
|
||||
)
|
||||
extra_field2 = TemplateField(
|
||||
name="index_name",
|
||||
field_type="str",
|
||||
required=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
value="",
|
||||
display_name="Index Name",
|
||||
)
|
||||
extra_fields.extend((extra_field, extra_field2))
|
||||
elif self.template.type_name == "SupabaseVectorStore":
|
||||
self.display_name = "Supabase"
|
||||
# Add table_name and query_name
|
||||
extra_field = TemplateField(
|
||||
name="table_name",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
)
|
||||
extra_field2 = TemplateField(
|
||||
name="query_name",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
)
|
||||
# Add supabase_url and supabase_service_key
|
||||
extra_field3 = TemplateField(
|
||||
name="supabase_url",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
)
|
||||
extra_field4 = TemplateField(
|
||||
name="supabase_service_key",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
)
|
||||
extra_fields.extend((extra_field, extra_field2, extra_field3, extra_field4))
|
||||
|
||||
elif self.template.type_name == "MongoDBAtlasVectorSearch":
|
||||
self.display_name = "MongoDB Atlas"
|
||||
|
||||
extra_field = TemplateField(
|
||||
name="mongodb_atlas_cluster_uri",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
display_name="MongoDB Atlas Cluster URI",
|
||||
value="",
|
||||
)
|
||||
extra_field2 = TemplateField(
|
||||
name="collection_name",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
display_name="Collection Name",
|
||||
value="",
|
||||
)
|
||||
extra_field3 = TemplateField(
|
||||
name="db_name",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
display_name="Database Name",
|
||||
value="",
|
||||
)
|
||||
extra_field4 = TemplateField(
|
||||
name="index_name",
|
||||
field_type="str",
|
||||
required=False,
|
||||
placeholder="",
|
||||
show=True,
|
||||
advanced=True,
|
||||
multiline=False,
|
||||
display_name="Index Name",
|
||||
value="",
|
||||
)
|
||||
extra_fields.extend((extra_field, extra_field2, extra_field3, extra_field4))
|
||||
|
||||
if extra_fields:
|
||||
for field in extra_fields:
|
||||
self.template.add_field(field)
|
||||
|
||||
def add_extra_base_classes(self) -> None:
|
||||
self.base_classes.append("BaseRetriever")
|
||||
self.base_classes.extend(("BaseRetriever", "VectorStoreRetriever"))
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
# Define common field attributes
|
||||
basic_fields = ["work_dir", "collection_name", "api_key", "location"]
|
||||
basic_fields = [
|
||||
"work_dir",
|
||||
"collection_name",
|
||||
"api_key",
|
||||
"location",
|
||||
"persist_directory",
|
||||
"persist",
|
||||
"weaviate_url",
|
||||
"index_name",
|
||||
"namespace",
|
||||
"folder_path",
|
||||
"table_name",
|
||||
"query_name",
|
||||
"supabase_url",
|
||||
"supabase_service_key",
|
||||
"mongodb_atlas_cluster_uri",
|
||||
"collection_name",
|
||||
"db_name",
|
||||
]
|
||||
advanced_fields = [
|
||||
"n_dim",
|
||||
"key",
|
||||
|
|
@ -43,14 +240,21 @@ class VectorStoreFrontendNode(FrontendNode):
|
|||
"https",
|
||||
"prefer_grpc",
|
||||
"grpc_port",
|
||||
"pinecone_api_key",
|
||||
"pinecone_env",
|
||||
"client_kwargs",
|
||||
"search_kwargs",
|
||||
]
|
||||
|
||||
# Check and set field attributes
|
||||
if field.name == "texts":
|
||||
# if field.name is "texts" it has to be replaced
|
||||
# when instantiating the vectorstores
|
||||
field.name = "documents"
|
||||
|
||||
field.field_type = "TextSplitter"
|
||||
field.display_name = "Text Splitter"
|
||||
field.required = True
|
||||
field.display_name = "Documents"
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
|
||||
|
|
@ -78,5 +282,6 @@ class VectorStoreFrontendNode(FrontendNode):
|
|||
field.advanced = True
|
||||
if "key" in field.name:
|
||||
field.password = False
|
||||
# TODO: Weaviate requires weaviate_url to be passed as it is not part of
|
||||
# the class or from_texts method. We need the add_extra_fields to fix this
|
||||
|
||||
elif field.name == "text_key":
|
||||
field.show = False
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from typing import Callable, Optional, Union
|
|||
from pydantic import BaseModel
|
||||
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.utils.constants import DIRECT_TYPES
|
||||
|
||||
|
||||
class Template(BaseModel):
|
||||
|
|
@ -18,8 +19,15 @@ class Template(BaseModel):
|
|||
for field in self.fields:
|
||||
format_field_func(field, name)
|
||||
|
||||
def sort_fields(self):
|
||||
# first sort alphabetically
|
||||
# then sort fields so that fields that have .field_type in DIRECT_TYPES are first
|
||||
self.fields.sort(key=lambda x: x.name)
|
||||
self.fields.sort(key=lambda x: x.field_type in DIRECT_TYPES, reverse=False)
|
||||
|
||||
def to_dict(self, format_field_func=None):
|
||||
self.process_fields(self.type_name, format_field_func)
|
||||
self.sort_fields()
|
||||
result = {field.name: field.to_dict() for field in self.fields}
|
||||
result["_type"] = self.type_name # type: ignore
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -36,3 +36,4 @@ def python_function(text: str) -> str:
|
|||
\"\"\"This is a default python function that returns the input text\"\"\"
|
||||
return text
|
||||
"""
|
||||
DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"]
|
||||
|
|
|
|||
|
|
@ -299,12 +299,15 @@ def format_dict(d, name: Optional[str] = None):
|
|||
if name == "OpenAI" and key == "model_name":
|
||||
value["options"] = constants.OPENAI_MODELS
|
||||
value["list"] = True
|
||||
value["value"] = constants.OPENAI_MODELS[0]
|
||||
elif name == "ChatOpenAI" and key == "model_name":
|
||||
value["options"] = constants.CHAT_OPENAI_MODELS
|
||||
value["list"] = True
|
||||
value["value"] = constants.CHAT_OPENAI_MODELS[0]
|
||||
elif (name == "Anthropic" or name == "ChatAnthropic") and key == "model_name":
|
||||
value["options"] = constants.ANTHROPIC_MODELS
|
||||
value["list"] = True
|
||||
value["value"] = constants.ANTHROPIC_MODELS[0]
|
||||
return d
|
||||
|
||||
|
||||
|
|
|
|||
36
src/frontend/package-lock.json
generated
36
src/frontend/package-lock.json
generated
|
|
@ -1212,15 +1212,15 @@
|
|||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "5.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.5.tgz",
|
||||
"integrity": "sha512-n0gzUxoZ2ZHZgnExkh2Htvo9uW2oakofgPRQrDoa/GQOWyRD0NH9MDszBwOb6AAoXZb+OV5TE7I4LeZ/dzgHYA==",
|
||||
"version": "5.13.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.6.tgz",
|
||||
"integrity": "sha512-G3Xr28uLqU3DyF6r2LQkHGw/ku4P0AHzlKVe7FGXOPl7X1u+hoe2xxj8Vdiq/69II/mh9OP21i38yBWgWb7WgQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"@babel/runtime": "^7.22.5",
|
||||
"@mui/private-theming": "^5.13.1",
|
||||
"@mui/styled-engine": "^5.13.2",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.13.1",
|
||||
"@mui/utils": "^5.13.6",
|
||||
"clsx": "^1.2.1",
|
||||
"csstype": "^3.1.2",
|
||||
"prop-types": "^15.8.1"
|
||||
|
|
@ -1280,11 +1280,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "5.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz",
|
||||
"integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==",
|
||||
"version": "5.13.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.6.tgz",
|
||||
"integrity": "sha512-ggNlxl5NPSbp+kNcQLmSig6WVB0Id+4gOxhx644987v4fsji+CSXc+MFYLocFB/x4oHtzCUlSzbVHlJfP/fXoQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"@babel/runtime": "^7.22.5",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"@types/react-is": "^18.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
|
|
@ -3714,9 +3714,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.2.1.tgz",
|
||||
"integrity": "sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
|
|
@ -5010,9 +5010,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.438",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.438.tgz",
|
||||
"integrity": "sha512-x94U0FhphEsHsOloCvlsujHCvoir0ZQ73ZAs/QN4PLx98uNvyEU79F75rq1db75Bx/atvuh7KPeuxelh+xfYJw=="
|
||||
"version": "1.4.440",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz",
|
||||
"integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
|
@ -6746,9 +6746,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.7",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.7.tgz",
|
||||
"integrity": "sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==",
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
|
||||
"integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
getRandomKeyByssmm,
|
||||
groupByFamily,
|
||||
isValidConnection,
|
||||
nodeIconsLucide,
|
||||
} from "../../../../utils";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import InputComponent from "../../../../components/inputComponent";
|
||||
|
|
@ -18,12 +19,13 @@ import InputFileComponent from "../../../../components/inputFileComponent";
|
|||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import IntComponent from "../../../../components/intComponent";
|
||||
import PromptAreaComponent from "../../../../components/promptComponent";
|
||||
import { nodeNames, nodeIcons } from "../../../../utils";
|
||||
import { nodeNames } from "../../../../utils";
|
||||
import React from "react";
|
||||
import { nodeColors } from "../../../../utils";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||
import { Info } from "lucide-react";
|
||||
|
||||
export default function ParameterComponent({
|
||||
left,
|
||||
|
|
@ -35,13 +37,15 @@ export default function ParameterComponent({
|
|||
type,
|
||||
name = "",
|
||||
required = false,
|
||||
info = "",
|
||||
}: ParameterComponentType) {
|
||||
const ref = useRef(null);
|
||||
const refHtml = useRef(null);
|
||||
const infoHtml = useRef(null);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const [position, setPosition] = useState(0);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const { setTabsState, tabId } = useContext(TabsContext);
|
||||
const { setTabsState, tabId, save } = useContext(TabsContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
|
||||
|
|
@ -78,9 +82,21 @@ export default function ParameterComponent({
|
|||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
infoHtml.current = (
|
||||
<div className="h-full w-full break-words">
|
||||
{info.split("\n").map((line, i) => (
|
||||
<p key={i} className="block">
|
||||
{line}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}, [info]);
|
||||
|
||||
useEffect(() => {
|
||||
const groupedObj = groupByFamily(myData, tooltipTitle);
|
||||
|
||||
|
||||
refHtml.current = groupedObj.map((item, i) => (
|
||||
<span
|
||||
key={getRandomKeyByssmm()}
|
||||
|
|
@ -89,12 +105,12 @@ export default function ParameterComponent({
|
|||
)}
|
||||
>
|
||||
<div
|
||||
className="h-5 w-5"
|
||||
className="h-6 w-6"
|
||||
style={{
|
||||
color: nodeColors[item.family],
|
||||
}}
|
||||
>
|
||||
{React.createElement(nodeIcons[item.family])}
|
||||
{React.createElement(nodeIconsLucide[item.family])}
|
||||
</div>
|
||||
<span className="ps-2 text-gray-950">
|
||||
{nodeNames[item.family] ?? ""}{" "}
|
||||
|
|
@ -105,7 +121,9 @@ export default function ParameterComponent({
|
|||
? item.type.split(", ").map((el, i) => (
|
||||
<React.Fragment key={el + i}>
|
||||
<span>
|
||||
{i === item.type.split(", ").length - 1 ? el : (el += `, `)}
|
||||
{i === item.type.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
{i % 2 === 0 && i > 0 && <br />}
|
||||
</React.Fragment>
|
||||
|
|
@ -115,7 +133,6 @@ export default function ParameterComponent({
|
|||
</span>
|
||||
</span>
|
||||
));
|
||||
|
||||
}, [tooltipTitle]);
|
||||
|
||||
return (
|
||||
|
|
@ -124,9 +141,22 @@ export default function ParameterComponent({
|
|||
className="w-full flex flex-wrap justify-between items-center bg-muted dark:bg-gray-800 dark:text-white mt-1 px-5 py-2"
|
||||
>
|
||||
<>
|
||||
<div className={"text-sm truncate w-full " + (left ? "" : "text-end")}>
|
||||
<div
|
||||
className={
|
||||
"text-sm truncate w-full" +
|
||||
(left ? "" : " text-end") +
|
||||
(info !== "" ? " flex items-center" : "")
|
||||
}
|
||||
>
|
||||
{title}
|
||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||
<div className="">
|
||||
{info !== "" && (
|
||||
<ShadTooltip content={infoHtml.current}>
|
||||
<Info className="ml-2 relative bottom-0.5 w-3 h-3" />
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{left &&
|
||||
(type === "str" ||
|
||||
|
|
@ -239,7 +269,8 @@ export default function ParameterComponent({
|
|||
fileTypes={data.node.template[name].fileTypes}
|
||||
suffixes={data.node.template[name].suffixes}
|
||||
onFileChange={(t: string) => {
|
||||
data.node.template[name].content = t;
|
||||
data.node.template[name].file_path = t;
|
||||
save();
|
||||
}}
|
||||
></InputFileComponent>
|
||||
) : left === true && type === "int" ? (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { classNames, nodeColors, nodeIcons, toTitleCase } from "../../utils";
|
||||
import {
|
||||
classNames,
|
||||
nodeColors,
|
||||
nodeIconsLucide,
|
||||
toTitleCase,
|
||||
} from "../../utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { useContext, useState, useEffect, useRef } from "react";
|
||||
|
|
@ -9,7 +14,6 @@ import NodeModal from "../../modals/NodeModal";
|
|||
import Tooltip from "../../components/TooltipComponent";
|
||||
import { NodeToolbar } from "reactflow";
|
||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import { useSSE } from "../../contexts/SSEContext";
|
||||
|
||||
|
|
@ -25,11 +29,13 @@ export default function GenericNode({
|
|||
const { types, deleteNode } = useContext(typesContext);
|
||||
|
||||
const { closePopUp, openPopUp } = useContext(PopUpContext);
|
||||
|
||||
const Icon = nodeIcons[data.type] || nodeIcons[types[data.type]];
|
||||
// any to avoid type conflict
|
||||
const Icon: any =
|
||||
nodeIconsLucide[data.type] || nodeIconsLucide[types[data.type]];
|
||||
const [validationStatus, setValidationStatus] = useState(null);
|
||||
// State for outline color
|
||||
const { sseData, isBuilding } = useSSE();
|
||||
const refHtml = useRef(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (reactFlowInstance) {
|
||||
|
|
@ -87,9 +93,11 @@ export default function GenericNode({
|
|||
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
<div className="ml-2 truncate">
|
||||
<ShadTooltip delayDuration={1500} content={data.type}>
|
||||
<div className="ml-2 truncate text-gray-800">{data.type}</div>
|
||||
<div className="ml-2 truncate flex">
|
||||
<ShadTooltip content={data.node.display_name}>
|
||||
<div className="ml-2 truncate text-gray-800">
|
||||
{data.node.display_name}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -193,6 +201,7 @@ export default function GenericNode({
|
|||
? toTitleCase(data.node.template[t].name)
|
||||
: toTitleCase(t)
|
||||
}
|
||||
info={data.node.template[t].info}
|
||||
name={t}
|
||||
tooltipTitle={data.node.template[t].type}
|
||||
required={data.node.template[t].required}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
import {
|
||||
XCircleIcon,
|
||||
XMarkIcon,
|
||||
InformationCircleIcon,
|
||||
CheckCircleIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { useState } from "react";
|
||||
import { SingleAlertComponentType } from "../../../../types/alerts";
|
||||
import { X, CheckCircle2, Info, XCircle } from "lucide-react";
|
||||
|
||||
export default function SingleAlert({
|
||||
dropItem,
|
||||
|
|
@ -34,7 +29,7 @@ export default function SingleAlert({
|
|||
key={dropItem.id}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<XCircleIcon
|
||||
<XCircle
|
||||
className="h-5 w-5 text-red-400 dark:text-red-50"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -70,7 +65,7 @@ export default function SingleAlert({
|
|||
className="inline-flex rounded-md bg-red-50 dark:bg-transparent p-1.5 text-red-500 dark:text-red-50"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
<X className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -81,7 +76,7 @@ export default function SingleAlert({
|
|||
key={dropItem.id}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<InformationCircleIcon
|
||||
<Info
|
||||
className="h-5 w-5 text-blue-400 dark:text-blue-50"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -116,7 +111,7 @@ export default function SingleAlert({
|
|||
className="inline-flex rounded-md bg-blue-50 dark:bg-transparent p-1.5 text-blue-500 dark:text-blue-50"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
<X className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -127,7 +122,7 @@ export default function SingleAlert({
|
|||
key={dropItem.id}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<CheckCircleIcon
|
||||
<CheckCircle2
|
||||
className="h-5 w-5 text-green-400 dark:text-green-50"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -150,7 +145,7 @@ export default function SingleAlert({
|
|||
className="inline-flex rounded-md bg-green-50 dark:bg-transparent p-1.5 text-green-500 dark:text-green-50"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
<X className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useContext, useEffect, useRef } from "react";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { XMarkIcon } from "@heroicons/react/24/solid";
|
||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import SingleAlert from "./components/singleAlertComponent";
|
||||
import { AlertDropdownType } from "../../types/alerts";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { useOnClickOutside } from "../hooks/useOnClickOutside";
|
||||
import { X, Trash2 } from "lucide-react";
|
||||
|
||||
export default function AlertDropdown({}: AlertDropdownType) {
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const componentRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -36,13 +36,13 @@ export default function AlertDropdown({}: AlertDropdownType) {
|
|||
setTimeout(clearNotificationList, 100);
|
||||
}}
|
||||
>
|
||||
<TrashIcon className="w-[1.1rem] h-[1.1rem]" />
|
||||
<Trash2 className="w-[1.1rem] h-[1.1rem]" />
|
||||
</button>
|
||||
<button
|
||||
className="text-gray-800 hover:text-red-500 dark:text-gray-200 dark:hover:text-red-500"
|
||||
onClick={closePopUp}
|
||||
>
|
||||
<XMarkIcon className="h-5 w-5" />
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { XCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ErrorAlertType } from "../../types/alerts";
|
||||
import { XCircle } from "lucide-react";
|
||||
|
||||
export default function ErrorAlert({
|
||||
title,
|
||||
|
|
@ -43,7 +43,7 @@ export default function ErrorAlert({
|
|||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<XCircleIcon
|
||||
<XCircle
|
||||
className="h-5 w-5 text-red-400 dark:text-red-50"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { InformationCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { NoticeAlertType } from "../../types/alerts";
|
||||
import { Info } from "lucide-react";
|
||||
|
||||
export default function NoticeAlert({
|
||||
title,
|
||||
|
|
@ -40,7 +40,7 @@ export default function NoticeAlert({
|
|||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<InformationCircleIcon
|
||||
<Info
|
||||
className="h-5 w-5 text-blue-400 dark:text-blue-50"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { CheckCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SuccessAlertType } from "../../types/alerts";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
|
||||
export default function SuccessAlert({
|
||||
title,
|
||||
|
|
@ -38,7 +38,7 @@ export default function SuccessAlert({
|
|||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<CheckCircleIcon
|
||||
<CheckCircle2
|
||||
className="h-5 w-5 text-green-400 dark:text-green-50"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Disclosure } from "@headlessui/react";
|
||||
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { classNames } from "../../utils";
|
||||
|
|
|
|||
6
src/frontend/src/components/LoadingSpinner/index.tsx
Normal file
6
src/frontend/src/components/LoadingSpinner/index.tsx
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { RadialProgressType } from "../../types/components";
|
||||
|
||||
export default function LoadingSpinner({}) {
|
||||
return <></>;
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { ShadTooltipProps } from "../../types/components";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
|
|
@ -5,18 +6,19 @@ import {
|
|||
TooltipTrigger,
|
||||
} from "../ui/tooltip";
|
||||
|
||||
const ShadTooltip = (props) => {
|
||||
const ShadTooltip = ({
|
||||
delayDuration = 500,
|
||||
side,
|
||||
content,
|
||||
children,
|
||||
}: ShadTooltipProps) => {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={props.delayDuration}>
|
||||
<TooltipTrigger asChild>{props.children}</TooltipTrigger>
|
||||
<Tooltip delayDuration={delayDuration}>
|
||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||
|
||||
<TooltipContent
|
||||
side={props.side}
|
||||
avoidCollisions={false}
|
||||
sticky="always"
|
||||
>
|
||||
{props.content}
|
||||
<TooltipContent side={side} avoidCollisions={false} sticky="always">
|
||||
{content}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
import {
|
||||
ChatBubbleLeftEllipsisIcon,
|
||||
ChatBubbleOvalLeftEllipsisIcon,
|
||||
PlusSmallIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { useState } from "react";
|
||||
import { ChatMessageType } from "../../../types/chat";
|
||||
import { nodeColors } from "../../../utils";
|
||||
import Convert from "ansi-to-html";
|
||||
const convert = new Convert({ newline: true });
|
||||
import { MessageCircle } from "lucide-react";
|
||||
|
||||
export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
|
||||
const [hidden, setHidden] = useState(true);
|
||||
|
|
@ -24,7 +20,7 @@ export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
|
|||
onClick={() => setHidden((prev) => !prev)}
|
||||
className="absolute top-2 right-2 cursor-pointer"
|
||||
>
|
||||
<ChatBubbleOvalLeftEllipsisIcon className="w-5 h-5 animate-bounce" />
|
||||
<MessageCircle className="w-5 h-5 animate-bounce" />
|
||||
</div>
|
||||
)}
|
||||
{chat.thought && chat.thought !== "" && !hidden && (
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import CodeAreaModal from "../../modals/codeAreaModal";
|
||||
import TextAreaModal from "../../modals/textAreaModal";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
export default function CodeAreaComponent({
|
||||
value,
|
||||
|
|
@ -69,7 +69,7 @@ export default function CodeAreaComponent({
|
|||
}}
|
||||
>
|
||||
{!editNode && (
|
||||
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-ring dark:text-gray-300 ml-3" />
|
||||
<ExternalLink className="w-6 h-6 hover:text-ring dark:text-gray-300 ml-3" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import { ChevronUpDownIcon, CheckIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useState } from "react";
|
||||
import { Fragment, useContext, useEffect, useState } from "react";
|
||||
import { DropDownComponentType } from "../../types/components";
|
||||
import { classNames } from "../../utils";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { ChevronsUpDown, Check } from "lucide-react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
|
||||
export default function Dropdown({
|
||||
value,
|
||||
|
|
@ -12,10 +14,16 @@ export default function Dropdown({
|
|||
editNode = false,
|
||||
numberOfOptions = 0,
|
||||
}: DropDownComponentType) {
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
|
||||
let [internalValue, setInternalValue] = useState(
|
||||
value === "" || !value ? "Choose an option" : value
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalValue(value === "" || !value ? "Choose an option" : value);
|
||||
}, [closePopUp]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Listbox
|
||||
|
|
@ -43,7 +51,7 @@ export default function Dropdown({
|
|||
"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||
}
|
||||
>
|
||||
<ChevronUpDownIcon
|
||||
<ChevronsUpDown
|
||||
className="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -97,7 +105,7 @@ export default function Dropdown({
|
|||
"absolute inset-y-0 right-0 flex items-center pr-4"
|
||||
)}
|
||||
>
|
||||
<CheckIcon
|
||||
<Check
|
||||
className={
|
||||
active
|
||||
? "h-5 w-5 dark:text-black text-black"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useContext, useEffect, useState } from "react";
|
|||
import { FloatComponentType } from "../../types/components";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
|
||||
export default function FloatComponent({
|
||||
value,
|
||||
|
|
@ -12,6 +13,7 @@ export default function FloatComponent({
|
|||
}: FloatComponentType) {
|
||||
const [myValue, setMyValue] = useState(value ?? "");
|
||||
const { setDisableCopyPaste } = useContext(TabsContext);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
|
||||
const step = 0.1;
|
||||
const min = 0;
|
||||
|
|
@ -26,7 +28,7 @@ export default function FloatComponent({
|
|||
|
||||
useEffect(() => {
|
||||
setMyValue(value);
|
||||
}, [value]);
|
||||
}, [closePopUp]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
onClick={() => {
|
||||
handleAddFlow();
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
New
|
||||
|
|
@ -77,6 +78,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
onClick={() => {
|
||||
openPopUp(<FlowSettingsModal />);
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Settings2 className="w-4 h-4 mr-2 dark:text-gray-300" />
|
||||
Settings
|
||||
|
|
@ -85,6 +87,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
onClick={() => {
|
||||
undo();
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Undo className="w-4 h-4 mr-2 dark:text-gray-300" />
|
||||
Undo
|
||||
|
|
@ -93,6 +96,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
onClick={() => {
|
||||
redo();
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<Redo className="w-4 h-4 mr-2 dark:text-gray-300" />
|
||||
Redo
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { Link, useLocation, useParams } from "react-router-dom";
|
|||
import { USER_PROJECTS_HEADER } from "../../constants";
|
||||
import { getRepoStars } from "../../controllers/API";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { Bell } from "lucide-react";
|
||||
|
||||
export default function Header() {
|
||||
const { flows, addFlow, tabId } = useContext(TabsContext);
|
||||
|
|
@ -134,7 +135,7 @@ export default function Header() {
|
|||
{notificationCenter && (
|
||||
<div className="absolute w-1.5 h-1.5 rounded-full bg-destructive right-[3px]"></div>
|
||||
)}
|
||||
<BellIcon className="h-5 w-5" aria-hidden="true" />
|
||||
<Bell className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { DocumentMagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { FileComponentType } from "../../types/components";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { FileSearch2 } from "lucide-react";
|
||||
import { uploadFile } from "../../controllers/API";
|
||||
|
||||
export default function InputFileComponent({
|
||||
value,
|
||||
|
|
@ -14,7 +16,9 @@ export default function InputFileComponent({
|
|||
editNode = false,
|
||||
}: FileComponentType) {
|
||||
const [myValue, setMyValue] = useState(value);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const { tabId } = useContext(TabsContext);
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setMyValue("");
|
||||
|
|
@ -23,12 +27,6 @@ export default function InputFileComponent({
|
|||
}
|
||||
}, [disabled, onChange]);
|
||||
|
||||
function attachFile(fileReadEvent: ProgressEvent<FileReader>) {
|
||||
fileReadEvent.preventDefault();
|
||||
const file = fileReadEvent.target.result;
|
||||
onFileChange(file as string);
|
||||
}
|
||||
|
||||
function checkFileType(fileName: string): boolean {
|
||||
for (let index = 0; index < suffixes.length; index++) {
|
||||
if (fileName.endsWith(suffixes[index])) {
|
||||
|
|
@ -43,27 +41,54 @@ export default function InputFileComponent({
|
|||
}, [value]);
|
||||
|
||||
const handleButtonClick = () => {
|
||||
// Create a file input element
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = suffixes.join(",");
|
||||
input.style.display = "none";
|
||||
input.multiple = false;
|
||||
input.style.display = "none"; // Hidden from view
|
||||
input.multiple = false; // Allow only one file selection
|
||||
|
||||
input.onchange = (e: Event) => {
|
||||
setLoading(true);
|
||||
|
||||
// Get the selected file
|
||||
const file = (e.target as HTMLInputElement).files?.[0];
|
||||
const fileData = new FileReader();
|
||||
fileData.onload = attachFile;
|
||||
|
||||
// Check if the file type is correct
|
||||
if (file && checkFileType(file.name)) {
|
||||
fileData.readAsDataURL(file);
|
||||
setMyValue(file.name);
|
||||
onChange(file.name);
|
||||
// Upload the file
|
||||
uploadFile(file, tabId)
|
||||
.then((res) => res.data)
|
||||
.then((data) => {
|
||||
console.log("File uploaded successfully");
|
||||
// Get the file name from the response
|
||||
const { file_path } = data;
|
||||
console.log("File name:", file_path);
|
||||
|
||||
// Update the state and callback with the name of the file
|
||||
// sets the value to the user
|
||||
setMyValue(file.name);
|
||||
onChange(file.name);
|
||||
// sets the value that goes to the backend
|
||||
onFileChange(file_path);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
console.error("Error occurred while uploading file");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
// Show an error if the file type is not allowed
|
||||
setErrorData({
|
||||
title:
|
||||
"Please select a valid file. Only files this files are allowed:",
|
||||
"Please select a valid file. Only these file types are allowed:",
|
||||
list: fileTypes,
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Trigger the file selection dialog
|
||||
input.click();
|
||||
};
|
||||
|
||||
|
|
@ -73,7 +98,7 @@ export default function InputFileComponent({
|
|||
disabled ? "pointer-events-none cursor-not-allowed w-full" : "w-full"
|
||||
}
|
||||
>
|
||||
<div className="w-full flex items-center gap-3">
|
||||
<div className="w-full flex items-center gap-2">
|
||||
<span
|
||||
onClick={handleButtonClick}
|
||||
className={
|
||||
|
|
@ -88,8 +113,11 @@ export default function InputFileComponent({
|
|||
{myValue !== "" ? myValue : "No file"}
|
||||
</span>
|
||||
<button onClick={handleButtonClick}>
|
||||
{!editNode && (
|
||||
<DocumentMagnifyingGlassIcon className="w-8 h-8 hover:text-ring" />
|
||||
{!editNode && !loading && (
|
||||
<FileSearch2 className="w-6 h-6 hover:text-ring" />
|
||||
)}
|
||||
{!editNode && loading && (
|
||||
<span className="loading loading-spinner loading-sm pl-3 h-8 pointer-events-none"></span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { InputListComponentType } from "../../types/components";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
|
||||
import _ from "lodash";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { X, Plus } from "lucide-react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
|
||||
export default function InputListComponent({
|
||||
value,
|
||||
onChange,
|
||||
|
|
@ -12,12 +14,19 @@ export default function InputListComponent({
|
|||
editNode = false,
|
||||
}: InputListComponentType) {
|
||||
const [inputList, setInputList] = useState(value ?? [""]);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setInputList([""]);
|
||||
onChange([""]);
|
||||
}
|
||||
}, [disabled, onChange]);
|
||||
|
||||
useEffect(() => {
|
||||
setInputList(value);
|
||||
}, [closePopUp]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
|
|
@ -43,9 +52,9 @@ export default function InputListComponent({
|
|||
setInputList((old) => {
|
||||
let newInputList = _.cloneDeep(old);
|
||||
newInputList[idx] = e.target.value;
|
||||
onChange(newInputList);
|
||||
return newInputList;
|
||||
});
|
||||
onChange(inputList);
|
||||
}}
|
||||
/>
|
||||
{idx === inputList.length - 1 ? (
|
||||
|
|
@ -59,7 +68,7 @@ export default function InputListComponent({
|
|||
onChange(inputList);
|
||||
}}
|
||||
>
|
||||
<PlusIcon className={"w-4 h-4 hover:text-ring"} />
|
||||
<Plus className={"w-4 h-4 hover:text-ring"} />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
|
|
@ -72,7 +81,7 @@ export default function InputListComponent({
|
|||
onChange(inputList);
|
||||
}}
|
||||
>
|
||||
<XMarkIcon className="w-4 h-4 hover:text-red-600" />
|
||||
<X className="w-4 h-4 hover:text-red-600" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { FloatComponentType } from "../../types/components";
|
|||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { classNames } from "../../utils";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
|
||||
export default function IntComponent({
|
||||
value,
|
||||
|
|
@ -14,6 +15,7 @@ export default function IntComponent({
|
|||
const [myValue, setMyValue] = useState(value ?? "");
|
||||
const { setDisableCopyPaste } = useContext(TabsContext);
|
||||
const min = 0;
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
|
|
@ -24,7 +26,7 @@ export default function IntComponent({
|
|||
|
||||
useEffect(() => {
|
||||
setMyValue(value);
|
||||
}, [value]);
|
||||
}, [closePopUp]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
import GenericModal from "../../modals/genericModal";
|
||||
import { TypeModal } from "../../utils";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
export default function PromptAreaComponent({
|
||||
value,
|
||||
|
|
@ -74,7 +74,7 @@ export default function PromptAreaComponent({
|
|||
}}
|
||||
>
|
||||
{!editNode && (
|
||||
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-ring dark:text-gray-300" />
|
||||
<ExternalLink className="w-6 h-6 hover:text-ring dark:text-gray-300" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
import GenericModal from "../../modals/genericModal";
|
||||
import { TypeModal } from "../../utils";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
export default function TextAreaComponent({
|
||||
value,
|
||||
onChange,
|
||||
|
|
@ -76,7 +77,7 @@ export default function TextAreaComponent({
|
|||
}}
|
||||
>
|
||||
{!editNode && (
|
||||
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-ring dark:text-gray-300" />
|
||||
<ExternalLink className="w-6 h-6 hover:text-ring dark:text-gray-300" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export const getPythonApiCode = (flow: FlowType): string => {
|
|||
|
||||
BASE_API_URL = "${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/predict"
|
||||
}/api/v1/process"
|
||||
FLOW_ID = "${flowId}"
|
||||
# You can tweak the flow by adding a tweaks dictionary
|
||||
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
|
||||
|
|
@ -83,7 +83,7 @@ def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
|
|||
"""
|
||||
api_url = f"{BASE_API_URL}/{flow_id}"
|
||||
|
||||
payload = {"message": message}
|
||||
payload = {"inputs": {"input": message}}
|
||||
|
||||
if tweaks:
|
||||
payload["tweaks"] = tweaks
|
||||
|
|
@ -106,9 +106,9 @@ export const getCurlCode = (flow: FlowType): string => {
|
|||
return `curl -X POST \\
|
||||
${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/predict/${flowId} \\
|
||||
}/api/v1/process/${flowId} \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-d '{"message": "Your message", "tweaks": ${JSON.stringify(
|
||||
-d '{"inputs": {"input": message}, "tweaks": ${JSON.stringify(
|
||||
tweaks,
|
||||
null,
|
||||
2
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from "../utils";
|
||||
import { alertContext } from "./alertContext";
|
||||
import { typesContext } from "./typesContext";
|
||||
import { APITemplateType } from "../types/api";
|
||||
import { APIClassType, APITemplateType } from "../types/api";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { addEdge } from "reactflow";
|
||||
import {
|
||||
|
|
@ -192,33 +192,49 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
function processFlowEdges(flow) {
|
||||
if (!flow.data || !flow.data.edges) return;
|
||||
flow.data.edges.forEach((edge) => {
|
||||
edge.className = "";
|
||||
edge.style = { stroke: "#555555" };
|
||||
});
|
||||
}
|
||||
|
||||
function updateDisplay_name(node: NodeType, template: APIClassType) {
|
||||
node.data.node.display_name = template["display_name"] || node.data.type;
|
||||
}
|
||||
|
||||
function updateNodeDocumentation(node: NodeType, template: APIClassType) {
|
||||
node.data.node.documentation = template["documentation"];
|
||||
}
|
||||
|
||||
function processFlowNodes(flow) {
|
||||
flow.data.nodes.forEach((node) => {
|
||||
if (!flow.data || !flow.data.nodes) return;
|
||||
flow.data.nodes.forEach((node: NodeType) => {
|
||||
const template = templates[node.data.type];
|
||||
if (!template) {
|
||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||
return;
|
||||
}
|
||||
if (Object.keys(template["template"]).length > 0) {
|
||||
updateDisplay_name(node, template);
|
||||
updateNodeBaseClasses(node, template);
|
||||
updateNodeEdges(flow, node, template);
|
||||
updateNodeDescription(node, template);
|
||||
updateNodeTemplate(node, template);
|
||||
updateNodeDocumentation(node, template);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateNodeBaseClasses(node, template) {
|
||||
function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
|
||||
node.data.node.base_classes = template["base_classes"];
|
||||
}
|
||||
|
||||
function updateNodeEdges(flow, node, template) {
|
||||
function updateNodeEdges(
|
||||
flow: FlowType,
|
||||
node: NodeType,
|
||||
template: APIClassType
|
||||
) {
|
||||
flow.data.edges.forEach((edge) => {
|
||||
if (edge.source === node.id) {
|
||||
edge.sourceHandle = edge.sourceHandle
|
||||
|
|
@ -230,11 +246,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
function updateNodeDescription(node, template) {
|
||||
function updateNodeDescription(node: NodeType, template: APIClassType) {
|
||||
node.data.node.description = template["description"];
|
||||
}
|
||||
|
||||
function updateNodeTemplate(node, template) {
|
||||
function updateNodeTemplate(node: NodeType, template: APIClassType) {
|
||||
node.data.node.template = updateTemplate(
|
||||
template["template"] as unknown as APITemplateType,
|
||||
node.data.node.template as APITemplateType
|
||||
|
|
@ -398,7 +414,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
y: insidePosition.y + n.position.y - minimumY,
|
||||
},
|
||||
data: {
|
||||
...n.data,
|
||||
..._.cloneDeep(n.data),
|
||||
id: newId,
|
||||
},
|
||||
};
|
||||
|
|
@ -463,6 +479,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
// Create a new flow with a default name if no flow is provided.
|
||||
const newFlow = createNewFlow(flowData, flow);
|
||||
processFlowEdges(newFlow);
|
||||
processFlowNodes(newFlow);
|
||||
|
||||
try {
|
||||
const { id } = await saveFlowToDatabase(newFlow);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
PromptTypeAPI,
|
||||
errorsTypeAPI,
|
||||
InitTypeAPI,
|
||||
UploadFileTypeAPI,
|
||||
} from "./../../types/api/index";
|
||||
import { APIObjectType, sendAllProps } from "../../types/api/index";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
|
|
@ -48,13 +49,6 @@ export async function postValidateCode(
|
|||
return await axios.post("/api/v1/validate/code", { code });
|
||||
}
|
||||
|
||||
export async function postValidateNode(
|
||||
nodeId: string,
|
||||
data: any
|
||||
): Promise<AxiosResponse<string>> {
|
||||
return await axios.post(`/api/v1/validate/node/${nodeId}`, { data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the prompt for the code block by sending it to an API endpoint.
|
||||
*
|
||||
|
|
@ -319,3 +313,21 @@ export async function postBuildInit(
|
|||
): Promise<AxiosResponse<InitTypeAPI>> {
|
||||
return await axios.post(`/api/v1/build/init`, flow);
|
||||
}
|
||||
|
||||
// fetch(`/upload/${id}`, {
|
||||
// method: "POST",
|
||||
// body: formData,
|
||||
// });
|
||||
/**
|
||||
* Uploads a file to the server.
|
||||
* @param {File} file - The file to upload.
|
||||
* @param {string} id - The ID of the flow to upload the file to.
|
||||
*/
|
||||
export async function uploadFile(
|
||||
file: File,
|
||||
id: string
|
||||
): Promise<AxiosResponse<UploadFileTypeAPI>> {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return await axios.post(`/api/v1/upload/${id}`, formData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import { ReactComponent as MidjorneySVG } from "./Midjourney_Emblem.svg";
|
||||
import { ReactComponent as MidjourneySVG } from "./Midjourney_Emblem.svg";
|
||||
|
||||
export const MidjorneyIcon = forwardRef<
|
||||
export const MidjourneyIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <MidjorneySVG ref={ref} {...props} />;
|
||||
return <MidjourneySVG ref={ref} {...props} />;
|
||||
});
|
||||
|
|
|
|||
9
src/frontend/src/icons/MongoDB/index.tsx
Normal file
9
src/frontend/src/icons/MongoDB/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import { ReactComponent as MongoDBSVG } from "./mongodb-icon.svg";
|
||||
|
||||
export const MongoDBIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <MongoDBSVG ref={ref} {...props} />;
|
||||
});
|
||||
1
src/frontend/src/icons/MongoDB/mongodb-icon.svg
Normal file
1
src/frontend/src/icons/MongoDB/mongodb-icon.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 32 32"><path d="M15.9.087l.854 1.604c.192.296.4.558.645.802.715.715 1.394 1.464 2.004 2.266 1.447 1.9 2.423 4.01 3.12 6.292.418 1.394.645 2.824.662 4.27.07 4.323-1.412 8.035-4.4 11.12-.488.488-1.01.94-1.57 1.342-.296 0-.436-.227-.558-.436-.227-.383-.366-.82-.436-1.255-.105-.523-.174-1.046-.14-1.586v-.244C16.057 24.21 15.796.21 15.9.087z" fill="#599636"/><path d="M15.9.034c-.035-.07-.07-.017-.105.017.017.35-.105.662-.296.96-.21.296-.488.523-.767.767-1.55 1.342-2.77 2.963-3.747 4.776-1.3 2.44-1.97 5.055-2.16 7.808-.087.993.314 4.497.627 5.508.854 2.684 2.388 4.933 4.375 6.885.488.47 1.01.906 1.55 1.325.157 0 .174-.14.21-.244a4.78 4.78 0 0 0 .157-.68l.35-2.614L15.9.034z" fill="#6cac48"/><path d="M16.754 28.845c.035-.4.227-.732.436-1.063-.21-.087-.366-.26-.488-.453-.105-.174-.192-.383-.26-.575-.244-.732-.296-1.5-.366-2.248v-.453c-.087.07-.105.662-.105.75a17.37 17.37 0 0 1-.314 2.353c-.052.314-.087.627-.28.906 0 .035 0 .07.017.122.314.924.4 1.865.453 2.824v.35c0 .418-.017.33.33.47.14.052.296.07.436.174.105 0 .122-.087.122-.157l-.052-.575v-1.604c-.017-.28.035-.558.07-.82z" fill="#c2bfbf"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
9
src/frontend/src/icons/Pinecone/index.tsx
Normal file
9
src/frontend/src/icons/Pinecone/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import { ReactComponent as PineconeSVG } from "./pinecone_logo.svg";
|
||||
|
||||
export const PineconeIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <PineconeSVG ref={ref} {...props} />;
|
||||
});
|
||||
21
src/frontend/src/icons/Pinecone/pinecone_logo.svg
Normal file
21
src/frontend/src/icons/Pinecone/pinecone_logo.svg
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<svg width="32" height="35" viewBox="0 0 32 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.8555 34.2962C14.9325 34.2962 15.8055 33.4451 15.8055 32.3954C15.8055 31.3456 14.9325 30.4946 13.8555 30.4946C12.7786 30.4946 11.9055 31.3456 11.9055 32.3954C11.9055 33.4451 12.7786 34.2962 13.8555 34.2962Z" fill="black"/>
|
||||
<path d="M18.4138 7.19675L19.2512 2.66005" stroke="black" stroke-width="2.11786" stroke-linecap="square"/>
|
||||
<path d="M22.2656 5.5855L19.3466 2.11099L15.3748 4.37292" stroke="black" stroke-width="2.11786" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M14.9202 26.5528L15.7337 22.0169" stroke="black" stroke-width="2.11786" stroke-linecap="square"/>
|
||||
<path d="M18.7729 24.9304L15.83 21.4671L11.8701 23.741" stroke="black" stroke-width="2.11786" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M16.6077 17.1996L17.4212 12.6633" stroke="black" stroke-width="2.11786" stroke-linecap="square"/>
|
||||
<path d="M20.4587 15.58L17.5277 12.128L13.5679 14.3904" stroke="black" stroke-width="2.11786" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M8.32871 26.1554L4.75171 28.5815" stroke="black" stroke-width="2.01017" stroke-linecap="square"/>
|
||||
<path d="M8.54383 30.0865L4.3208 28.8738L4.63185 24.5944" stroke="black" stroke-width="2.01017" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M21.3213 28.4299L23.8096 31.9282" stroke="black" stroke-width="2.01017" stroke-linecap="square"/>
|
||||
<path d="M19.718 32.045L24.1085 32.3365L25.3527 28.2438" stroke="black" stroke-width="2.01017" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M25.3999 21.3291L29.7784 22.0996" stroke="black" stroke-width="2.05804" stroke-linecap="square"/>
|
||||
<path d="M26.9072 25.072L30.3048 22.1919L28.1634 18.3557" stroke="black" stroke-width="2.05804" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M24.1196 12.8615L28.0197 10.763" stroke="black" stroke-width="2.05804" stroke-linecap="square"/>
|
||||
<path d="M24.3357 8.83965L28.4869 10.5188L27.7093 14.8216" stroke="black" stroke-width="2.05804" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M6.91639 18.1572L2.52588 17.4101" stroke="black" stroke-width="2.05804" stroke-linecap="square"/>
|
||||
<path d="M4.17731 21.1645L2 17.328L5.36167 14.436" stroke="black" stroke-width="2.05804" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M11.0799 10.6129L8.14893 7.34769" stroke="black" stroke-width="2.05804" stroke-linecap="square"/>
|
||||
<path d="M12.2897 6.77496L7.80349 6.96156L7.01392 11.2649" stroke="black" stroke-width="2.05804" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
9
src/frontend/src/icons/supabase/index.tsx
Normal file
9
src/frontend/src/icons/supabase/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import { ReactComponent as SupabaseSvg } from "./supabase-icon.svg";
|
||||
|
||||
export const SupabaseIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <SupabaseSvg ref={ref} {...props} />;
|
||||
});
|
||||
99
src/frontend/src/icons/supabase/supabase-icon.svg
Normal file
99
src/frontend/src/icons/supabase/supabase-icon.svg
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64"
|
||||
version="1.1"
|
||||
id="svg20"
|
||||
sodipodi:docname="supabase-icon.svg"
|
||||
style="fill:none"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata24">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1687"
|
||||
inkscape:window-height="849"
|
||||
id="namedview22"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0884956"
|
||||
inkscape:cx="54.5"
|
||||
inkscape:cy="56.5"
|
||||
inkscape:window-x="70"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg20" />
|
||||
<path
|
||||
d="m 37.41219,62.936701 c -1.634985,2.05896 -4.950068,0.93085 -4.989463,-1.69817 L 31.846665,22.786035 h 25.855406 c 4.683108,0 7.294967,5.409033 4.382927,9.07673 z"
|
||||
id="path2"
|
||||
style="fill:url(#paint0_linear);stroke-width:0.57177335"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 37.41219,62.936701 c -1.634985,2.05896 -4.950068,0.93085 -4.989463,-1.69817 L 31.846665,22.786035 h 25.855406 c 4.683108,0 7.294967,5.409033 4.382927,9.07673 z"
|
||||
id="path4"
|
||||
style="fill:url(#paint1_linear);fill-opacity:0.2;stroke-width:0.57177335"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 26.89694,1.0634102 c 1.634986,-2.05918508 4.950125,-0.93090008 4.989521,1.698149 L 32.138899,41.214003 H 6.607076 c -4.6832501,0 -7.29518376,-5.409032 -4.3830007,-9.07673 z"
|
||||
id="path6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#3ecf8e;stroke-width:0.57177335" />
|
||||
<defs
|
||||
id="defs18">
|
||||
<linearGradient
|
||||
id="paint0_linear"
|
||||
x1="53.973801"
|
||||
y1="54.973999"
|
||||
x2="94.163498"
|
||||
y2="71.829498"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.57177306,0,0,0.57177334,0.98590077,-0.12074988)">
|
||||
<stop
|
||||
stop-color="#249361"
|
||||
id="stop8" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#3ECF8E"
|
||||
id="stop10" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear"
|
||||
x1="36.1558"
|
||||
y1="30.577999"
|
||||
x2="54.484402"
|
||||
y2="65.080597"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.57177306,0,0,0.57177334,0.98590077,-0.12074988)">
|
||||
<stop
|
||||
id="stop13" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop15" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -1,4 +1,3 @@
|
|||
import { CodeBracketSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import "ace-builds/src-noconflict/mode-python";
|
||||
|
|
@ -26,7 +25,7 @@ import {
|
|||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "../../components/ui/tabs";
|
||||
import { Check, Clipboard } from "lucide-react";
|
||||
import { Check, Clipboard, Code2 } from "lucide-react";
|
||||
|
||||
export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -88,7 +87,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
|||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<span className="pr-2">Code</span>
|
||||
<CodeBracketSquareIcon
|
||||
<Code2
|
||||
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -104,7 +103,9 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
|||
<div className="flex items-center justify-between px-2">
|
||||
<TabsList>
|
||||
{tabs.map((tab, index) => (
|
||||
<TabsTrigger value={index.toString()}>{tab.name}</TabsTrigger>
|
||||
<TabsTrigger key={index} value={index.toString()}>
|
||||
{tab.name}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
<div className="float-right">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
TableRow,
|
||||
} from "../../components/ui/table";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
import { VariableIcon } from "@heroicons/react/24/outline";
|
||||
import InputListComponent from "../../components/inputListComponent";
|
||||
import TextAreaComponent from "../../components/textAreaComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
|
|
@ -33,6 +32,7 @@ import {
|
|||
} from "../../components/ui/dialog";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Variable } from "lucide-react";
|
||||
|
||||
export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -80,7 +80,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
|
||||
return (
|
||||
<Dialog open={true} onOpenChange={setModalOpen}>
|
||||
<DialogTrigger></DialogTrigger>
|
||||
<DialogTrigger asChild></DialogTrigger>
|
||||
<DialogContent className="lg:max-w-[700px] ">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
|
|
@ -90,9 +90,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
<DialogDescription>
|
||||
{data.node?.description}
|
||||
<div className="flex pt-4">
|
||||
<VariableIcon className="w-5 h-5 pe-1 text-gray-700 stroke-2 dark:text-slate-200">
|
||||
|
||||
</VariableIcon>
|
||||
<Variable className="w-5 h-5 pe-1 text-gray-700 stroke-2 dark:text-slate-200"></Variable>
|
||||
<span className="text-sm font-semibold text-gray-800 dark:text-white">
|
||||
Parameters
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ export default function ModalField({
|
|||
data.node.template[name].value = t;
|
||||
setEnabled(t);
|
||||
}}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : type === "float" ? (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
|
|
@ -13,6 +12,7 @@ import {
|
|||
} from "../../utils";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import ModalField from "./components/ModalField";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
export default function NodeModal({ data }: { data: NodeDataType }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -27,7 +27,8 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
|
|||
}, 300);
|
||||
}
|
||||
}
|
||||
const Icon = nodeIcons[types[data.type]];
|
||||
// any to avoid type conflict
|
||||
const Icon: any = nodeIcons[types[data.type]];
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
|
|
@ -69,7 +70,7 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
|
|||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
<X className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { LockClosedIcon, PaperAirplaneIcon } from "@heroicons/react/24/outline";
|
||||
import { classNames } from "../../../utils";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { TabsContext } from "../../../contexts/tabsContext";
|
||||
import { INPUT_STYLE } from "../../../constants";
|
||||
import { Lock, Send } from "lucide-react";
|
||||
|
||||
export default function ChatInput({
|
||||
lockChat,
|
||||
chatValue,
|
||||
|
|
@ -14,10 +15,7 @@ export default function ChatInput({
|
|||
if (!lockChat && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
},[
|
||||
lockChat,inputRef
|
||||
])
|
||||
|
||||
}, [lockChat, inputRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
|
|
@ -63,12 +61,12 @@ export default function ChatInput({
|
|||
<div className="absolute bottom-0.5 right-3">
|
||||
<button disabled={lockChat} onClick={() => sendMessage()}>
|
||||
{lockChat ? (
|
||||
<LockClosedIcon
|
||||
<Lock
|
||||
className="h-5 w-5 text-gray-500 dark:hover:text-gray-300 animate-pulse"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<PaperAirplaneIcon
|
||||
<Send
|
||||
className="h-5 w-5 text-gray-500 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ChatMessageType } from "../../../types/chat";
|
||||
import { classNames } from "../../../utils";
|
||||
import AiIcon from "../../../assets/Gooey Ring-5s-271px.svg";
|
||||
import AiIconStill from "../../../assets/froze-flow.png";
|
||||
import { UserIcon } from "@heroicons/react/24/solid";
|
||||
import FileCard from "../fileComponent";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import rehypeMathjax from "rehype-mathjax";
|
||||
|
|
@ -12,6 +10,7 @@ import remarkGfm from "remark-gfm";
|
|||
import remarkMath from "remark-math";
|
||||
import { CodeBlock } from "./codeBlock";
|
||||
import Convert from "ansi-to-html";
|
||||
import { User2, MessageCircle } from "lucide-react";
|
||||
|
||||
export default function ChatMessage({
|
||||
chat,
|
||||
|
|
@ -62,7 +61,7 @@ export default function ChatMessage({
|
|||
</div>
|
||||
)}
|
||||
{chat.isSend && (
|
||||
<UserIcon className="w-6 h-6 -mb-1 text-gray-800 dark:text-gray-200" />
|
||||
<User2 className="w-6 h-6 -mb-1 text-gray-800 dark:text-gray-200" />
|
||||
)}
|
||||
</div>
|
||||
{!chat.isSend ? (
|
||||
|
|
@ -73,7 +72,7 @@ export default function ChatMessage({
|
|||
onClick={() => setHidden((prev) => !prev)}
|
||||
className="absolute -top-1 -left-2 cursor-pointer"
|
||||
>
|
||||
<ChatBubbleOvalLeftEllipsisIcon className="w-5 h-5 animate-bounce dark:text-white" />
|
||||
<MessageCircle className="w-5 h-5 animate-bounce dark:text-white" />
|
||||
</div>
|
||||
)}
|
||||
{chat.thought && chat.thought !== "" && !hidden && (
|
||||
|
|
@ -152,7 +151,7 @@ export default function ChatMessage({
|
|||
</div>
|
||||
) : (
|
||||
<div className="w-full flex items-center">
|
||||
<div className="text-start inline-block px-3 text-sm text-gray-600 dark:text-white">
|
||||
<div className="text-start inline-block px-3 text-gray-600 dark:text-white">
|
||||
<span
|
||||
className="text-gray-600 dark:text-gray-200"
|
||||
dangerouslySetInnerHTML={{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CloudArrowDownIcon, DocumentIcon } from "@heroicons/react/24/outline";
|
||||
import * as base64js from "base64-js";
|
||||
import { useState } from "react";
|
||||
import { DownloadCloud, File } from "lucide-react";
|
||||
|
||||
export default function FileCard({ fileName, content, fileType }) {
|
||||
const handleDownload = () => {
|
||||
|
|
@ -43,7 +43,7 @@ export default function FileCard({ fileName, content, fileType }) {
|
|||
className="text-gray-500 py-1 px-2 dark:bg-gray-700 dark:text-gray-300"
|
||||
onClick={handleDownload}
|
||||
>
|
||||
<CloudArrowDownIcon className="hover:scale-110 w-5 h-5 text-current"></CloudArrowDownIcon>
|
||||
<DownloadCloud className="hover:scale-110 w-5 h-5 text-current" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -65,14 +65,14 @@ export default function FileCard({ fileName, content, fileType }) {
|
|||
className="w-8 h-8"
|
||||
/>
|
||||
) : (
|
||||
<DocumentIcon className="w-8 h-8" />
|
||||
<File className="w-8 h-8" />
|
||||
)}
|
||||
<div className="flex flex-col items-start">
|
||||
{" "}
|
||||
<div className="truncate text-sm text-current">{fileName}</div>
|
||||
<div className="truncate text-xs text-gray-500">{fileType}</div>
|
||||
</div>
|
||||
<CloudArrowDownIcon className="w-6 h-6 text-current ml-auto" />
|
||||
<DownloadCloud className="w-6 h-6 text-current ml-auto" />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useEffect, useRef, useState } from "react";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { validateNodes } from "../../utils";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import ChatMessage from "./chatMessage";
|
||||
import { Eraser } from "lucide-react";
|
||||
import { X } from "lucide-react";
|
||||
import { X, MessagesSquare, Eraser } from "lucide-react";
|
||||
import { sendAllProps } from "../../types/api";
|
||||
import { ChatMessageType } from "../../types/chat";
|
||||
import ChatInput from "./chatInput";
|
||||
|
|
@ -387,7 +385,7 @@ export default function ChatModal({
|
|||
<span className="text-base text-gray-500">
|
||||
Start a conversation and click the agent’s thoughts{" "}
|
||||
<span>
|
||||
<ChatBubbleOvalLeftEllipsisIcon className="w-6 h-6 inline animate-bounce " />
|
||||
<MessagesSquare className="w-5 h-5 inline animate-bounce mx-1 " />
|
||||
</span>{" "}
|
||||
to inspect the chaining process.
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { XMarkIcon, CommandLineIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import AceEditor from "react-ace";
|
||||
|
|
@ -22,6 +21,7 @@ import {
|
|||
} from "../../components/ui/dialog";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants";
|
||||
import { TerminalSquare } from "lucide-react";
|
||||
|
||||
export default function CodeAreaModal({
|
||||
value,
|
||||
|
|
@ -51,7 +51,7 @@ export default function CodeAreaModal({
|
|||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<span className="pr-2">Edit Code</span>
|
||||
<CommandLineIcon
|
||||
<TerminalSquare
|
||||
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useRef, useState } from "react";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
|
|
@ -17,6 +16,7 @@ import {
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { PROMPT_DIALOG_SUBTITLE, TEXT_DIALOG_SUBTITLE } from "../../constants";
|
||||
import { FileText } from "lucide-react";
|
||||
|
||||
export default function GenericModal({
|
||||
value,
|
||||
|
|
@ -54,7 +54,7 @@ export default function GenericModal({
|
|||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<span className="pr-2">{myModalTitle}</span>
|
||||
<DocumentTextIcon
|
||||
<FileText
|
||||
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import React, { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { DocumentDuplicateIcon } from "@heroicons/react/solid";
|
||||
import React, { ReactNode } from "react";
|
||||
import { classNames } from "../../../utils";
|
||||
import Tooltip from "../../../components/TooltipComponent";
|
||||
|
||||
export default function ButtonBox({
|
||||
onClick,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ChevronRightIcon } from "@heroicons/react/24/solid";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
import { DisclosureComponentType } from "../../../../types/components";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
|
||||
export default function DisclosureComponent({
|
||||
button: { title, Icon, buttons = [] },
|
||||
|
|
@ -14,7 +14,7 @@ export default function DisclosureComponent({
|
|||
<div>
|
||||
<Disclosure.Button className="select-none bg-muted dark:bg-gray-700/60 dark:border-y-gray-600 w-full flex justify-between items-center -mt-px px-3 py-2 border-y border-y-gray-200">
|
||||
<div className="flex gap-4">
|
||||
<Icon className="w-6 text-gray-800 dark:text-white/80" />
|
||||
<Icon size={22} className="text-gray-800 dark:text-white/80" />
|
||||
<span className="flex items-center text-sm text-gray-800 dark:text-white/80 font-medium">
|
||||
{title}
|
||||
</span>
|
||||
|
|
@ -26,7 +26,7 @@ export default function DisclosureComponent({
|
|||
</button>
|
||||
))}
|
||||
<div>
|
||||
<ChevronRightIcon
|
||||
<ChevronRight
|
||||
className={`${
|
||||
open || openDisc ? "rotate-90 transform" : ""
|
||||
} h-4 w-4 text-gray-800 dark:text-white`}
|
||||
|
|
|
|||
|
|
@ -398,7 +398,8 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
zoomOnDoubleClick={!disableCopyPaste}
|
||||
selectNodesOnDrag={false}
|
||||
className="theme-attribution"
|
||||
minZoom={0.05}
|
||||
minZoom={0.01}
|
||||
maxZoom={8}
|
||||
>
|
||||
<Background className="dark:bg-gray-900" />
|
||||
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
import { Bars2Icon } from "@heroicons/react/24/outline";
|
||||
import DisclosureComponent from "../DisclosureComponent";
|
||||
import {
|
||||
classNames,
|
||||
nodeColors,
|
||||
nodeIcons,
|
||||
nodeIconsLucide,
|
||||
nodeNames,
|
||||
} from "../../../../utils";
|
||||
import { useContext, useState } from "react";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
import { APIClassType, APIObjectType } from "../../../../types/api";
|
||||
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import { Code2, FileDown, FileUp, Save } from "lucide-react";
|
||||
import { Code2, FileDown, FileUp, Save, Search } from "lucide-react";
|
||||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||
import ExportModal from "../../../../modals/exportModal";
|
||||
import ApiModal from "../../../../modals/ApiModal";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import { alertContext } from "../../../../contexts/alertContext";
|
||||
import { updateFlowInDatabase } from "../../../../controllers/API";
|
||||
import { INPUT_STYLE } from "../../../../constants";
|
||||
import { Separator } from "../../../../components/ui/separator";
|
||||
import { Menu } from "lucide-react";
|
||||
|
||||
export default function ExtraSidebar() {
|
||||
const { data } = useContext(typesContext);
|
||||
|
|
@ -64,7 +62,7 @@ export default function ExtraSidebar() {
|
|||
return (
|
||||
<div className="w-52 flex flex-col overflow-hidden scrollbar-hide h-full border-r">
|
||||
<div className="mt-2 mb-2 w-full flex gap-2 justify-between px-2 items-center">
|
||||
<ShadTooltip delayDuration={1000} content="Import" side="top">
|
||||
<ShadTooltip content="Import" side="top">
|
||||
<button
|
||||
className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center rounded-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
|
||||
onClick={() => {
|
||||
|
|
@ -76,7 +74,7 @@ export default function ExtraSidebar() {
|
|||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip delayDuration={1000} content="Export" side="top">
|
||||
<ShadTooltip content="Export" side="top">
|
||||
<button
|
||||
className={classNames(
|
||||
"hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md"
|
||||
|
|
@ -88,7 +86,7 @@ export default function ExtraSidebar() {
|
|||
<FileDown className="w-5 h-5 dark:text-gray-300"></FileDown>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
<ShadTooltip delayDuration={1000} content="Code" side="top">
|
||||
<ShadTooltip content="Code" side="top">
|
||||
<button
|
||||
className={classNames(
|
||||
"hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md"
|
||||
|
|
@ -101,7 +99,7 @@ export default function ExtraSidebar() {
|
|||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip delayDuration={1000} content="Save" side="top">
|
||||
<ShadTooltip content="Save" side="top">
|
||||
<button
|
||||
className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center transition-all shadow-sm duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md"
|
||||
onClick={(event) => {
|
||||
|
|
@ -135,7 +133,7 @@ export default function ExtraSidebar() {
|
|||
}}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex py-1.5 pr-3 items-center">
|
||||
<MagnifyingGlassIcon className="h-5 w-5 dark:text-white"></MagnifyingGlassIcon>
|
||||
<Search size={20} color="#000000" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -146,10 +144,10 @@ export default function ExtraSidebar() {
|
|||
Object.keys(dataFilter[d]).length > 0 ? (
|
||||
<DisclosureComponent
|
||||
openDisc={search.length == 0 ? false : true}
|
||||
key={i}
|
||||
key={nodeNames[d]}
|
||||
button={{
|
||||
title: nodeNames[d] ?? nodeNames.unknown,
|
||||
Icon: nodeIcons[d] ?? nodeIcons.unknown,
|
||||
Icon: nodeIconsLucide[d] ?? nodeIconsLucide.unknown,
|
||||
}}
|
||||
>
|
||||
<div className="p-2 flex flex-col gap-2">
|
||||
|
|
@ -157,10 +155,9 @@ export default function ExtraSidebar() {
|
|||
.sort()
|
||||
.map((t: string, k) => (
|
||||
<ShadTooltip
|
||||
content={t}
|
||||
delayDuration={1500}
|
||||
content={data[d][t].display_name}
|
||||
side="right"
|
||||
key={i}
|
||||
key={data[d][t].display_name}
|
||||
>
|
||||
<div key={k} data-tooltip-id={t}>
|
||||
<div
|
||||
|
|
@ -186,9 +183,9 @@ export default function ExtraSidebar() {
|
|||
>
|
||||
<div className="flex w-full justify-between text-sm px-3 py-1 bg-white dark:bg-gray-800 items-center border-dashed border-gray-400 dark:border-gray-600 border-l-0 rounded-md rounded-l-none border">
|
||||
<span className="text-black dark:text-white w-full pr-1 truncate text-xs">
|
||||
{t}
|
||||
{data[d][t].display_name}
|
||||
</span>
|
||||
<Bars2Icon className="w-4 h-6 text-gray-400 dark:text-gray-600" />
|
||||
<Menu className="w-4 h-6 text-gray-400 dark:text-gray-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useContext, useState } from "react";
|
||||
import { Settings2, Copy, Trash2 } from "lucide-react";
|
||||
import { Settings2, Copy, Trash2, FileText } from "lucide-react";
|
||||
import { classNames } from "../../../../utils";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import { useReactFlow } from "reactflow";
|
||||
|
|
@ -29,23 +29,21 @@ const NodeToolbarComponent = (props) => {
|
|||
<>
|
||||
<div className="h-10 w-26">
|
||||
<span className="isolate inline-flex rounded-md shadow-sm">
|
||||
<ShadTooltip delayDuration={1000} content="Delete" side="top">
|
||||
<ShadTooltip content="Delete" side="top">
|
||||
<button
|
||||
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative inline-flex items-center rounded-l-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
|
||||
className="hover:dark:hover:bg-[#242f47] text-foreground transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative inline-flex items-center rounded-l-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
|
||||
onClick={() => {
|
||||
props.deleteNode(props.data.id);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="w-4 h-4 dark:text-gray-300"></Trash2>
|
||||
<Trash2 className="w-4 h-4 dark:text-muted-foreground"></Trash2>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip delayDuration={1000} content="Duplicate" side="top">
|
||||
<ShadTooltip content="Duplicate" side="top">
|
||||
<button
|
||||
className={classNames(
|
||||
nodeLength > 0
|
||||
? "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
|
||||
: "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md"
|
||||
"hover:dark:hover:bg-[#242f47] text-foreground transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
|
@ -64,31 +62,66 @@ const NodeToolbarComponent = (props) => {
|
|||
);
|
||||
}}
|
||||
>
|
||||
<Copy className="w-4 h-4 dark:text-gray-300"></Copy>
|
||||
<Copy className="w-4 h-4 dark:text-muted-foreground"></Copy>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
{nodeLength > 0 && (
|
||||
<ShadTooltip delayDuration={1000} content="Edit" side="top">
|
||||
<button
|
||||
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md"
|
||||
onClick={(event) => {
|
||||
<ShadTooltip
|
||||
content={
|
||||
props.data.node.documentation === ""
|
||||
? "Coming Soon"
|
||||
: "Documentation"
|
||||
}
|
||||
side="top"
|
||||
>
|
||||
<a
|
||||
className={classNames(
|
||||
"hover:dark:hover:bg-[#242f47] transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10" +
|
||||
(props.data.node.documentation === ""
|
||||
? " text-muted-foreground"
|
||||
: " text-foreground")
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={props.data.node.documentation}
|
||||
// deactivate link if no documentation is provided
|
||||
onClick={(event) => {
|
||||
if (props.data.node.documentation === "") {
|
||||
event.preventDefault();
|
||||
props.openPopUp(<EditNodeModal data={props.data} />);
|
||||
}}
|
||||
>
|
||||
<Settings2 className="w-4 h-4 dark:text-gray-300"></Settings2>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FileText className="w-4 h-4 dark:text-muted-foreground"></FileText>
|
||||
</a>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<button
|
||||
className={classNames(
|
||||
"hover:dark:hover:bg-[#242f47] transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md" +
|
||||
(nodeLength == 0
|
||||
? " text-muted-foreground"
|
||||
: " text-foreground")
|
||||
)}
|
||||
onClick={(event) => {
|
||||
if (nodeLength == 0) {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.preventDefault();
|
||||
props.openPopUp(<EditNodeModal data={props.data} />);
|
||||
}}
|
||||
>
|
||||
<Settings2 className="w-4 h-4 dark:text-muted-foreground"></Settings2>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
{/*
|
||||
<Menu as="div" className="relative inline-block text-left z-100">
|
||||
<button className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md">
|
||||
<button className="hover:dark:hover:bg-[#242f47] text-foreground transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md">
|
||||
<div>
|
||||
<Menu.Button className="flex items-center">
|
||||
<EllipsisVerticalIcon
|
||||
className="w-5 h-5 dark:text-gray-300"
|
||||
className="w-5 h-5 dark:text-muted-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Menu.Button>
|
||||
|
|
@ -117,7 +150,7 @@ const NodeToolbarComponent = (props) => {
|
|||
className={classNames(
|
||||
active
|
||||
? "bg-muted text-gray-900"
|
||||
: "text-gray-700",
|
||||
: "text-foreground",
|
||||
"w-full group flex items-center px-4 py-2 text-sm"
|
||||
)}
|
||||
>
|
||||
|
|
@ -157,7 +190,7 @@ const NodeToolbarComponent = (props) => {
|
|||
className={classNames(
|
||||
active
|
||||
? "bg-muted text-gray-900"
|
||||
: "text-gray-700",
|
||||
: "text-foreground",
|
||||
"w-full group flex items-center px-4 py-2 text-sm"
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ export type APIClassType = {
|
|||
base_classes: Array<string>;
|
||||
description: string;
|
||||
template: APITemplateType;
|
||||
display_name: string;
|
||||
documentation: string;
|
||||
[key: string]: Array<string> | string | APITemplateType;
|
||||
};
|
||||
export type TemplateVariableType = {
|
||||
|
|
@ -46,3 +48,8 @@ export type BuildStatusTypeAPI = {
|
|||
export type InitTypeAPI = {
|
||||
flowId: string;
|
||||
};
|
||||
|
||||
export type UploadFileTypeAPI = {
|
||||
file_path: string;
|
||||
flowId: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ForwardRefExoticComponent, ReactElement, ReactNode } from "react";
|
||||
import { ReactElement, ReactNode } from "react";
|
||||
import { NodeDataType } from "../flow/index";
|
||||
import { typesContextType } from "../typesContext";
|
||||
export type InputComponentType = {
|
||||
|
|
@ -35,6 +35,7 @@ export type ParameterComponentType = {
|
|||
name?: string;
|
||||
tooltipTitle: string;
|
||||
dataContext?: typesContextType;
|
||||
info?: string;
|
||||
};
|
||||
export type InputListComponentType = {
|
||||
value: string[];
|
||||
|
|
@ -65,7 +66,7 @@ export type DisclosureComponentType = {
|
|||
openDisc: boolean;
|
||||
button: {
|
||||
title: string;
|
||||
Icon: ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>;
|
||||
Icon: any;
|
||||
buttons?: {
|
||||
Icon: ReactElement;
|
||||
title: string;
|
||||
|
|
@ -109,3 +110,12 @@ export type RadialProgressType = {
|
|||
value?: number;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export type Side = "top" | "right" | "bottom" | "left";
|
||||
|
||||
export type ShadTooltipProps = {
|
||||
delayDuration?: number;
|
||||
side?: Side;
|
||||
content: ReactNode;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { HomeIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
export type sidebarNavigationItemType = {
|
||||
name: string;
|
||||
href: string;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { ReactFlowInstance } from "reactflow";
|
||||
import { APIClassType } from "../api";
|
||||
|
||||
const types: { [char: string]: string } = {};
|
||||
const template: { [char: string]: string } = {};
|
||||
const template: { [char: string]: APIClassType } = {};
|
||||
const data: { [char: string]: string } = {};
|
||||
|
||||
export type typesContextType = {
|
||||
|
|
|
|||
|
|
@ -33,15 +33,37 @@ import { HackerNewsIcon } from "./icons/hackerNews";
|
|||
import { HugginFaceIcon } from "./icons/HuggingFace";
|
||||
import { IFixIcon } from "./icons/IFixIt";
|
||||
import { MetaIcon } from "./icons/Meta";
|
||||
import { MidjorneyIcon } from "./icons/Midjorney";
|
||||
import { MidjourneyIcon } from "./icons/Midjorney";
|
||||
import { NotionIcon } from "./icons/Notion";
|
||||
import { OpenAiIcon } from "./icons/OpenAi";
|
||||
import { QDrantIcon } from "./icons/QDrant";
|
||||
import { SearxIcon } from "./icons/Searx";
|
||||
import { SlackIcon } from "./icons/Slack";
|
||||
import { PineconeIcon } from "./icons/Pinecone";
|
||||
import clsx, { ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./constants";
|
||||
import { ComponentType, SVGProps } from "react";
|
||||
import {
|
||||
Cpu,
|
||||
Fingerprint,
|
||||
Gift,
|
||||
Hammer,
|
||||
HelpCircle,
|
||||
Laptop2,
|
||||
Layers,
|
||||
Lightbulb,
|
||||
Link,
|
||||
MessageCircle,
|
||||
Paperclip,
|
||||
Rocket,
|
||||
Scissors,
|
||||
TerminalSquare,
|
||||
Wand2,
|
||||
Wrench,
|
||||
} from "lucide-react";
|
||||
import { SupabaseIcon } from "./icons/supabase";
|
||||
import { MongoDBIcon } from "./icons/MongoDB";
|
||||
|
||||
export function classNames(...classes: Array<string>) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
|
|
@ -165,11 +187,14 @@ export const nodeIcons: {
|
|||
HuggingFaceEmbeddings: HugginFaceIcon,
|
||||
IFixitLoader: IFixIcon,
|
||||
Meta: MetaIcon,
|
||||
Midjorney: MidjorneyIcon,
|
||||
Midjourney: MidjourneyIcon,
|
||||
NotionDirectoryLoader: NotionIcon,
|
||||
ChatOpenAI: OpenAiIcon,
|
||||
OpenAI: OpenAiIcon,
|
||||
OpenAIEmbeddings: OpenAiIcon,
|
||||
Pinecone: PineconeIcon,
|
||||
SupabaseVectorStore: SupabaseIcon,
|
||||
MongoDBAtlasVectorSearch: MongoDBIcon,
|
||||
// UnstructuredPowerPointLoader: PowerPointIcon, // word and powerpoint have differente styles
|
||||
Qdrant: QDrantIcon,
|
||||
// ReadTheDocsLoader: ReadTheDocsIcon, // does not work
|
||||
|
|
@ -198,6 +223,151 @@ export const nodeIcons: {
|
|||
unknown: QuestionMarkCircleIcon,
|
||||
};
|
||||
|
||||
export const nodeIconsLucide: {
|
||||
[char: string]: React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>;
|
||||
} = {
|
||||
Chroma: ChromaIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
AirbyteJSONLoader: AirbyteIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Anthropic: AnthropicIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
ChatAnthropic: AnthropicIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
BingSearchAPIWrapper: BingIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
BingSearchRun: BingIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Cohere: CohereIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
CohereEmbeddings: CohereIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
EverNoteLoader: EvernoteIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
FacebookChatLoader: FBIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
GitbookLoader: GitBookIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
GoogleSearchAPIWrapper: GoogleIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
GoogleSearchResults: GoogleIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
GoogleSearchRun: GoogleIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
HNLoader: HackerNewsIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
HuggingFaceHub: HugginFaceIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
HuggingFaceEmbeddings: HugginFaceIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
IFixitLoader: IFixIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Meta: MetaIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Midjorney: MidjourneyIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
MongoDBAtlasVectorSearch: MongoDBIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
NotionDirectoryLoader: NotionIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
ChatOpenAI: OpenAiIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
OpenAI: OpenAiIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
OpenAIEmbeddings: OpenAiIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Pinecone: PineconeIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Qdrant: QDrantIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
Searx: SearxIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
SlackDirectoryLoader: SlackIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
SupabaseVectorStore: SupabaseIcon as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
agents: Rocket as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
chains: Link as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
memories: Cpu as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
llms: Lightbulb as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
prompts: TerminalSquare as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
tools: Wrench as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
advanced: Laptop2 as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
chat: MessageCircle as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
embeddings: Fingerprint as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
documentloaders: Paperclip as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
vectorstores: Layers as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
toolkits: Hammer as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
textsplitters: Scissors as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
wrappers: Gift as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
utilities: Wand2 as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
unknown: HelpCircle as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
};
|
||||
|
||||
export const gradients = [
|
||||
"bg-gradient-to-br from-gray-800 via-rose-700 to-violet-900",
|
||||
"bg-gradient-to-br from-green-200 via-green-300 to-blue-500",
|
||||
|
|
@ -403,9 +573,7 @@ export function toFirstUpperCase(str: string) {
|
|||
}
|
||||
|
||||
export function snakeToSpaces(str: string) {
|
||||
let result = str.split("_").join(" ");
|
||||
|
||||
return result;
|
||||
return str.split("_").join(" ");
|
||||
}
|
||||
|
||||
export function toNormalCase(str: string) {
|
||||
|
|
@ -449,10 +617,7 @@ export function roundNumber(x: number, decimals: number) {
|
|||
export function getConnectedNodes(edge: Edge, nodes: Array<Node>): Array<Node> {
|
||||
const sourceId = edge.source;
|
||||
const targetId = edge.target;
|
||||
const connectedNodes = nodes.filter(
|
||||
(node) => node.id === targetId || node.id === sourceId
|
||||
);
|
||||
return connectedNodes;
|
||||
return nodes.filter((node) => node.id === targetId || node.id === sourceId);
|
||||
}
|
||||
|
||||
export function isValidConnection(
|
||||
|
|
@ -699,7 +864,7 @@ export function groupByFamily(data, baseClasses) {
|
|||
return foundIndex === index;
|
||||
});
|
||||
|
||||
let groupedObj = groupedBy.reduce((result, item) => {
|
||||
return groupedBy.reduce((result, item) => {
|
||||
const existingGroup = result.find((group) => group.family === item.family);
|
||||
|
||||
if (existingGroup) {
|
||||
|
|
@ -710,8 +875,6 @@ export function groupByFamily(data, baseClasses) {
|
|||
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
return groupedObj;
|
||||
}
|
||||
|
||||
export function buildTweaks(flow) {
|
||||
|
|
@ -752,9 +915,7 @@ export function validateNode(
|
|||
)
|
||||
? [
|
||||
`${type} is missing ${
|
||||
template.display_name
|
||||
? template.display_name
|
||||
: toNormalCase(template[t].name)
|
||||
template.display_name || toNormalCase(template[t].name)
|
||||
}.`,
|
||||
]
|
||||
: []
|
||||
|
|
@ -813,9 +974,9 @@ export function getRandomName(
|
|||
return toTitleCase(final_name);
|
||||
}
|
||||
|
||||
export function getRandomKeyByssmm(): string{
|
||||
export function getRandomKeyByssmm(): string {
|
||||
const now = new Date();
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
|
||||
return seconds + milliseconds;
|
||||
const seconds = String(now.getSeconds()).padStart(2, "0");
|
||||
const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
|
||||
return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -26,6 +26,7 @@ def test_zero_shot_agent(client: TestClient):
|
|||
"type": "LLMChain",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["allowed_tools"] == {
|
||||
"required": False,
|
||||
|
|
@ -37,6 +38,7 @@ def test_zero_shot_agent(client: TestClient):
|
|||
"type": "Tool",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -60,6 +62,7 @@ def test_json_agent(client: TestClient):
|
|||
"type": "BaseToolkit",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["llm"] == {
|
||||
"required": True,
|
||||
|
|
@ -72,6 +75,7 @@ def test_json_agent(client: TestClient):
|
|||
"list": False,
|
||||
"advanced": False,
|
||||
"display_name": "LLM",
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -97,8 +101,9 @@ def test_csv_agent(client: TestClient):
|
|||
"name": "path",
|
||||
"type": "file",
|
||||
"list": False,
|
||||
"content": None,
|
||||
"file_path": None,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["llm"] == {
|
||||
"required": True,
|
||||
|
|
@ -111,6 +116,7 @@ def test_csv_agent(client: TestClient):
|
|||
"list": False,
|
||||
"advanced": False,
|
||||
"display_name": "LLM",
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -137,11 +143,13 @@ def test_initialize_agent(client: TestClient):
|
|||
"self-ask-with-search",
|
||||
"conversational-react-description",
|
||||
"openai-functions",
|
||||
"openai-multi-functions",
|
||||
],
|
||||
"name": "agent",
|
||||
"type": "str",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["memory"] == {
|
||||
"required": False,
|
||||
|
|
@ -153,6 +161,7 @@ def test_initialize_agent(client: TestClient):
|
|||
"type": "BaseChatMemory",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["tools"] == {
|
||||
"required": False,
|
||||
|
|
@ -164,6 +173,7 @@ def test_initialize_agent(client: TestClient):
|
|||
"type": "Tool",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["llm"] == {
|
||||
"required": True,
|
||||
|
|
@ -176,4 +186,5 @@ def test_initialize_agent(client: TestClient):
|
|||
"list": False,
|
||||
"advanced": False,
|
||||
"display_name": "LLM",
|
||||
"info": "",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ def test_conversation_chain(client: TestClient):
|
|||
"type": "BaseMemory",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["verbose"] == {
|
||||
"required": False,
|
||||
|
|
@ -49,6 +50,7 @@ def test_conversation_chain(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["llm"] == {
|
||||
"required": True,
|
||||
|
|
@ -60,6 +62,7 @@ def test_conversation_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["input_key"] == {
|
||||
"required": True,
|
||||
|
|
@ -72,6 +75,7 @@ def test_conversation_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["output_key"] == {
|
||||
"required": True,
|
||||
|
|
@ -84,6 +88,7 @@ def test_conversation_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["_type"] == "ConversationChain"
|
||||
|
||||
|
|
@ -120,6 +125,7 @@ def test_llm_chain(client: TestClient):
|
|||
"type": "BaseMemory",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["verbose"] == {
|
||||
"required": False,
|
||||
|
|
@ -132,6 +138,7 @@ def test_llm_chain(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["llm"] == {
|
||||
"required": True,
|
||||
|
|
@ -143,6 +150,7 @@ def test_llm_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["output_key"] == {
|
||||
"required": True,
|
||||
|
|
@ -155,6 +163,7 @@ def test_llm_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -184,6 +193,7 @@ def test_llm_checker_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["_type"] == "LLMCheckerChain"
|
||||
|
||||
|
|
@ -217,6 +227,7 @@ def test_llm_math_chain(client: TestClient):
|
|||
"type": "BaseMemory",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["verbose"] == {
|
||||
"required": False,
|
||||
|
|
@ -229,6 +240,7 @@ def test_llm_math_chain(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["llm"] == {
|
||||
"required": True,
|
||||
|
|
@ -240,6 +252,7 @@ def test_llm_math_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["input_key"] == {
|
||||
"required": True,
|
||||
|
|
@ -252,6 +265,7 @@ def test_llm_math_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["output_key"] == {
|
||||
"required": True,
|
||||
|
|
@ -264,6 +278,7 @@ def test_llm_math_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["_type"] == "LLMMathChain"
|
||||
|
||||
|
|
@ -304,6 +319,7 @@ def test_series_character_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["character"] == {
|
||||
"required": True,
|
||||
|
|
@ -315,6 +331,7 @@ def test_series_character_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["series"] == {
|
||||
"required": True,
|
||||
|
|
@ -326,6 +343,7 @@ def test_series_character_chain(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["_type"] == "SeriesCharacterChain"
|
||||
|
||||
|
|
@ -367,6 +385,7 @@ def test_mid_journey_prompt_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
# Test the description object
|
||||
assert (
|
||||
|
|
@ -406,6 +425,7 @@ def test_time_travel_guide_chain(client: TestClient):
|
|||
"type": "BaseLanguageModel",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["memory"] == {
|
||||
"required": False,
|
||||
|
|
@ -417,6 +437,7 @@ def test_time_travel_guide_chain(client: TestClient):
|
|||
"type": "BaseChatMemory",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
|
||||
assert chain["description"] == "Time travel guide chain."
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ def test_template_field_defaults(sample_template_field: TemplateField):
|
|||
assert sample_template_field.value is None
|
||||
assert sample_template_field.suffixes == []
|
||||
assert sample_template_field.file_types == []
|
||||
assert sample_template_field.content is None
|
||||
assert sample_template_field.file_path is None
|
||||
assert sample_template_field.password is False
|
||||
assert sample_template_field.name == "test_field"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from typing import Type, Union
|
||||
from langflow.graph.edge.base import Edge
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
|
|
@ -236,6 +238,7 @@ def test_build_params(basic_graph):
|
|||
root = get_root_node(basic_graph)
|
||||
# Root node is a TimeTravelGuideChain
|
||||
# which requires an llm and memory
|
||||
assert root is not None
|
||||
assert isinstance(root.params, dict)
|
||||
assert "llm" in root.params
|
||||
assert "memory" in root.params
|
||||
|
|
@ -269,7 +272,6 @@ def test_tool_node_build(complex_graph):
|
|||
assert tool_node is not None
|
||||
built_object = tool_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the ToolNode's build() method
|
||||
|
||||
|
||||
def test_chain_node_build(complex_graph):
|
||||
|
|
@ -277,7 +279,6 @@ def test_chain_node_build(complex_graph):
|
|||
assert chain_node is not None
|
||||
built_object = chain_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the ChainNode's build() method
|
||||
|
||||
|
||||
def test_prompt_node_build(complex_graph):
|
||||
|
|
@ -285,7 +286,6 @@ def test_prompt_node_build(complex_graph):
|
|||
assert prompt_node is not None
|
||||
built_object = prompt_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the PromptNode's build() method
|
||||
|
||||
|
||||
def test_llm_node_build(basic_graph):
|
||||
|
|
@ -293,23 +293,36 @@ def test_llm_node_build(basic_graph):
|
|||
assert llm_node is not None
|
||||
built_object = llm_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the LLMNode's build() method
|
||||
|
||||
|
||||
def test_toolkit_node_build(openapi_graph):
|
||||
# Write a file to the disk
|
||||
file_path = "api-with-examples.yaml"
|
||||
with open(file_path, "w") as f:
|
||||
f.write("openapi: 3.0.0")
|
||||
|
||||
toolkit_node = get_node_by_type(openapi_graph, ToolkitVertex)
|
||||
assert toolkit_node is not None
|
||||
built_object = toolkit_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the ToolkitNode's build() method
|
||||
# Remove the file
|
||||
os.remove(file_path)
|
||||
assert not Path(file_path).exists()
|
||||
|
||||
|
||||
def test_file_tool_node_build(openapi_graph):
|
||||
file_path = "api-with-examples.yaml"
|
||||
with open(file_path, "w") as f:
|
||||
f.write("openapi: 3.0.0")
|
||||
|
||||
assert Path(file_path).exists()
|
||||
file_tool_node = get_node_by_type(openapi_graph, FileToolVertex)
|
||||
assert file_tool_node is not None
|
||||
built_object = file_tool_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the FileToolNode's build() method
|
||||
# Remove the file
|
||||
os.remove(file_path)
|
||||
assert not Path(file_path).exists()
|
||||
|
||||
|
||||
def test_wrapper_node_build(openapi_graph):
|
||||
|
|
@ -317,7 +330,6 @@ def test_wrapper_node_build(openapi_graph):
|
|||
assert wrapper_node is not None
|
||||
built_object = wrapper_node.build()
|
||||
assert built_object is not None
|
||||
# Add any further assertions specific to the WrapperNode's build() method
|
||||
|
||||
|
||||
def test_get_result_and_thought(basic_graph):
|
||||
|
|
@ -325,7 +337,7 @@ def test_get_result_and_thought(basic_graph):
|
|||
responses = [
|
||||
"Final Answer: I am a response",
|
||||
]
|
||||
message = "Hello"
|
||||
message = {"input": "Hello"}
|
||||
# Find the node that is an LLMNode and change the
|
||||
# _built_object to a FakeListLLM
|
||||
llm_node = get_node_by_type(basic_graph, LLMVertex)
|
||||
|
|
@ -338,8 +350,5 @@ def test_get_result_and_thought(basic_graph):
|
|||
# now build again and check if FakeListLLM was used
|
||||
|
||||
# Get the result and thought
|
||||
result, thought = get_result_and_thought(langchain_object, message)
|
||||
# The result should be a str
|
||||
assert isinstance(result, str)
|
||||
# The thought should be a Thought
|
||||
assert isinstance(thought, str)
|
||||
result = get_result_and_thought(langchain_object, message)
|
||||
assert isinstance(result, dict)
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ def test_openai(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["verbose"] == {
|
||||
"required": False,
|
||||
|
|
@ -132,6 +133,7 @@ def test_openai(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["client"] == {
|
||||
"required": False,
|
||||
|
|
@ -143,6 +145,7 @@ def test_openai(client: TestClient):
|
|||
"type": "Any",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["model_name"] == {
|
||||
"required": False,
|
||||
|
|
@ -162,6 +165,7 @@ def test_openai(client: TestClient):
|
|||
"type": "str",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
# Add more assertions for other properties here
|
||||
assert template["temperature"] == {
|
||||
|
|
@ -175,6 +179,7 @@ def test_openai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["max_tokens"] == {
|
||||
"required": False,
|
||||
|
|
@ -187,6 +192,7 @@ def test_openai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["top_p"] == {
|
||||
"required": False,
|
||||
|
|
@ -199,6 +205,7 @@ def test_openai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["frequency_penalty"] == {
|
||||
"required": False,
|
||||
|
|
@ -211,6 +218,7 @@ def test_openai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["presence_penalty"] == {
|
||||
"required": False,
|
||||
|
|
@ -223,6 +231,7 @@ def test_openai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["n"] == {
|
||||
"required": False,
|
||||
|
|
@ -235,6 +244,7 @@ def test_openai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["best_of"] == {
|
||||
"required": False,
|
||||
|
|
@ -247,6 +257,7 @@ def test_openai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["model_kwargs"] == {
|
||||
"required": False,
|
||||
|
|
@ -258,6 +269,7 @@ def test_openai(client: TestClient):
|
|||
"type": "code",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["openai_api_key"] == {
|
||||
"required": False,
|
||||
|
|
@ -271,6 +283,7 @@ def test_openai(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["batch_size"] == {
|
||||
"required": False,
|
||||
|
|
@ -283,6 +296,7 @@ def test_openai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["request_timeout"] == {
|
||||
"required": False,
|
||||
|
|
@ -294,6 +308,7 @@ def test_openai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["logit_bias"] == {
|
||||
"required": False,
|
||||
|
|
@ -305,6 +320,7 @@ def test_openai(client: TestClient):
|
|||
"type": "code",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["max_retries"] == {
|
||||
"required": False,
|
||||
|
|
@ -317,6 +333,7 @@ def test_openai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["streaming"] == {
|
||||
"required": False,
|
||||
|
|
@ -329,6 +346,7 @@ def test_openai(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -352,6 +370,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["client"] == {
|
||||
"required": False,
|
||||
|
|
@ -363,13 +382,14 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "Any",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["model_name"] == {
|
||||
"required": False,
|
||||
"placeholder": "",
|
||||
"show": True,
|
||||
"multiline": False,
|
||||
"value": "gpt-3.5-turbo",
|
||||
"value": "gpt-3.5-turbo-0613",
|
||||
"password": False,
|
||||
"options": [
|
||||
"gpt-3.5-turbo-0613",
|
||||
|
|
@ -385,6 +405,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "str",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["temperature"] == {
|
||||
"required": False,
|
||||
|
|
@ -397,6 +418,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["model_kwargs"] == {
|
||||
"required": False,
|
||||
|
|
@ -408,6 +430,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "code",
|
||||
"list": False,
|
||||
"advanced": True,
|
||||
"info": "",
|
||||
}
|
||||
assert template["openai_api_key"] == {
|
||||
"required": False,
|
||||
|
|
@ -421,6 +444,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["request_timeout"] == {
|
||||
"required": False,
|
||||
|
|
@ -432,6 +456,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "float",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["max_retries"] == {
|
||||
"required": False,
|
||||
|
|
@ -444,6 +469,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["streaming"] == {
|
||||
"required": False,
|
||||
|
|
@ -456,6 +482,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["n"] == {
|
||||
"required": False,
|
||||
|
|
@ -468,6 +495,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
|
||||
assert template["max_tokens"] == {
|
||||
|
|
@ -480,6 +508,7 @@ def test_chat_open_ai(client: TestClient):
|
|||
"type": "int",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["_type"] == "ChatOpenAI"
|
||||
assert (
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ def test_prompt_template(client: TestClient):
|
|||
"type": "str",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["output_parser"] == {
|
||||
"required": False,
|
||||
|
|
@ -39,6 +40,7 @@ def test_prompt_template(client: TestClient):
|
|||
"type": "BaseOutputParser",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["partial_variables"] == {
|
||||
"required": False,
|
||||
|
|
@ -50,6 +52,7 @@ def test_prompt_template(client: TestClient):
|
|||
"type": "code",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["template"] == {
|
||||
"required": True,
|
||||
|
|
@ -61,6 +64,7 @@ def test_prompt_template(client: TestClient):
|
|||
"type": "prompt",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["template_format"] == {
|
||||
"required": False,
|
||||
|
|
@ -73,6 +77,7 @@ def test_prompt_template(client: TestClient):
|
|||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["validate_template"] == {
|
||||
"required": False,
|
||||
|
|
@ -85,85 +90,7 @@ def test_prompt_template(client: TestClient):
|
|||
"type": "bool",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
}
|
||||
|
||||
|
||||
def test_few_shot_prompt_template(client: TestClient):
|
||||
response = client.get("api/v1/all")
|
||||
assert response.status_code == 200
|
||||
json_response = response.json()
|
||||
prompts = json_response["prompts"]
|
||||
|
||||
prompt = prompts["FewShotPromptTemplate"]
|
||||
template = prompt["template"]
|
||||
# Test other fields in the template similar to PromptTemplate
|
||||
assert template["examples"] == {
|
||||
"required": False,
|
||||
"placeholder": "",
|
||||
"show": True,
|
||||
"multiline": True,
|
||||
"password": False,
|
||||
"name": "examples",
|
||||
"type": "prompt",
|
||||
"list": True,
|
||||
"advanced": False,
|
||||
}
|
||||
assert template["example_selector"] == {
|
||||
"required": False,
|
||||
"placeholder": "",
|
||||
"show": False,
|
||||
"multiline": False,
|
||||
"password": False,
|
||||
"name": "example_selector",
|
||||
"type": "BaseExampleSelector",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
}
|
||||
assert template["example_prompt"] == {
|
||||
"required": True,
|
||||
"placeholder": "",
|
||||
"show": True,
|
||||
"multiline": False,
|
||||
"password": False,
|
||||
"name": "example_prompt",
|
||||
"type": "PromptTemplate",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
}
|
||||
assert template["suffix"] == {
|
||||
"required": True,
|
||||
"placeholder": "",
|
||||
"show": True,
|
||||
"multiline": True,
|
||||
"password": False,
|
||||
"name": "suffix",
|
||||
"type": "prompt",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
}
|
||||
assert template["example_separator"] == {
|
||||
"required": False,
|
||||
"placeholder": "",
|
||||
"show": False,
|
||||
"multiline": False,
|
||||
"value": "\n\n",
|
||||
"password": False,
|
||||
"name": "example_separator",
|
||||
"type": "str",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
}
|
||||
assert template["prefix"] == {
|
||||
"required": False,
|
||||
"placeholder": "",
|
||||
"show": True,
|
||||
"multiline": True,
|
||||
"value": "",
|
||||
"password": False,
|
||||
"name": "prefix",
|
||||
"type": "prompt",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -185,6 +112,7 @@ def test_zero_shot_prompt(client: TestClient):
|
|||
"type": "prompt",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["suffix"] == {
|
||||
"required": True,
|
||||
|
|
@ -197,6 +125,7 @@ def test_zero_shot_prompt(client: TestClient):
|
|||
"type": "prompt",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
assert template["format_instructions"] == {
|
||||
"required": True,
|
||||
|
|
@ -209,4 +138,5 @@ def test_zero_shot_prompt(client: TestClient):
|
|||
"type": "prompt",
|
||||
"list": False,
|
||||
"advanced": False,
|
||||
"info": "",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ def test_build_template_from_function():
|
|||
# Test with valid name
|
||||
result = build_template_from_function("ExampleClass1", type_to_loader_dict)
|
||||
|
||||
assert result is not None
|
||||
assert "template" in result
|
||||
assert "description" in result
|
||||
assert "base_classes" in result
|
||||
|
|
@ -67,6 +68,7 @@ def test_build_template_from_function():
|
|||
result_with_function = build_template_from_function(
|
||||
"ExampleClass1", type_to_loader_dict, add_function=True
|
||||
)
|
||||
assert result_with_function is not None
|
||||
assert "function" in result_with_function["base_classes"]
|
||||
|
||||
# Test with invalid name
|
||||
|
|
@ -80,6 +82,7 @@ def test_build_template_from_class():
|
|||
|
||||
# Test valid input
|
||||
result = build_template_from_class("Child", type_to_cls_dict)
|
||||
assert result is not None
|
||||
assert "template" in result
|
||||
assert "description" in result
|
||||
assert "base_classes" in result
|
||||
|
|
@ -222,6 +225,7 @@ def test_format_dict():
|
|||
"password": False,
|
||||
"multiline": False,
|
||||
"options": OPENAI_MODELS,
|
||||
"value": "text-davinci-003",
|
||||
},
|
||||
}
|
||||
expected_output_openai_chat = {
|
||||
|
|
@ -233,6 +237,7 @@ def test_format_dict():
|
|||
"password": False,
|
||||
"multiline": False,
|
||||
"options": CHAT_OPENAI_MODELS,
|
||||
"value": "gpt-3.5-turbo-0613",
|
||||
},
|
||||
}
|
||||
assert format_dict(input_dict, "OpenAI") == expected_output_openai
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue