merge conflicts

This commit is contained in:
Cristhian Zanforlin Lousa 2023-07-03 09:09:10 -03:00
commit b27ec98b62
92 changed files with 4266 additions and 2099 deletions

1
.gitignore vendored
View file

@ -242,3 +242,4 @@ dmypy.json
# Poetry # Poetry
.testenv/* .testenv/*
langflow.db langflow.db
langchain.db

1437
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.2.2" version = "0.2.10"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
maintainers = [ maintainers = [
@ -22,25 +22,24 @@ include = ["src/backend/langflow/*", "src/backend/langflow/**/*"]
langflow = "langflow.__main__:main" langflow = "langflow.__main__:main"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = ">=3.9,<3.12" python = ">=3.9,<3.11"
fastapi = "^0.98.0" fastapi = "^0.98.0"
uvicorn = "^0.22.0" uvicorn = "^0.22.0"
beautifulsoup4 = "^4.11.2" beautifulsoup4 = "^4.12.2"
google-search-results = "^2.4.1" google-search-results = "^2.4.1"
google-api-python-client = "^2.79.0" google-api-python-client = "^2.79.0"
typer = "^0.9.0" typer = "^0.9.0"
gunicorn = "^20.1.0" gunicorn = "^20.1.0"
langchain = "^0.0.215" langchain = "^0.0.219"
openai = "^0.27.8" openai = "^0.27.8"
types-pyyaml = "^6.0.12.8" pandas = "^2.0.0"
pandas = "^1.5.3"
chromadb = "^0.3.21" chromadb = "^0.3.21"
huggingface-hub = "^0.13.3" huggingface-hub = "^0.15.0"
rich = "^13.4.2" rich = "^13.4.2"
llama-cpp-python = "~0.1.0" llama-cpp-python = "~0.1.0"
networkx = "^3.1" networkx = "^3.1"
unstructured = "^0.5.11" unstructured = "^0.7.0"
pypdf = "^3.7.1" pypdf = "^3.11.0"
lxml = "^4.9.2" lxml = "^4.9.2"
pysrt = "^1.1.2" pysrt = "^1.1.2"
fake-useragent = "^1.1.3" fake-useragent = "^1.1.3"
@ -49,18 +48,18 @@ psycopg2-binary = "^2.9.6"
pyarrow = "^12.0.0" pyarrow = "^12.0.0"
tiktoken = "~0.4.0" tiktoken = "~0.4.0"
wikipedia = "^1.4.0" wikipedia = "^1.4.0"
langchain-serve = { version = ">0.0.39", optional = true } langchain-serve = { version = ">0.0.47", optional = true }
qdrant-client = "^1.2.0" qdrant-client = "^1.3.0"
websockets = "^10.3" websockets = "^10.3"
weaviate-client = "^3.21.0" weaviate-client = "^3.21.0"
jina = "3.15.2" jina = "3.15.2"
sentence-transformers = "^2.2.2" sentence-transformers = "^2.2.2"
ctransformers = "^0.2.2" ctransformers = "^0.2.10"
cohere = "^4.6.0" cohere = "^4.11.0"
python-multipart = "^0.0.6" python-multipart = "^0.0.6"
sqlmodel = "^0.0.8" sqlmodel = "^0.0.8"
faiss-cpu = "^1.7.4" faiss-cpu = "^1.7.4"
anthropic = "^0.2.10" anthropic = "^0.3.0"
orjson = "^3.9.1" orjson = "^3.9.1"
multiprocess = "^0.70.14" multiprocess = "^0.70.14"
cachetools = "^5.3.1" cachetools = "^5.3.1"
@ -70,7 +69,9 @@ pinecone-client = "^2.2.2"
supabase = "^1.0.3" supabase = "^1.0.3"
pymongo = "^4.4.0" pymongo = "^4.4.0"
certifi = "^2023.5.7" certifi = "^2023.5.7"
google-cloud-aiplatform = "^1.26.1"
psycopg = "^3.1.9"
psycopg-binary = "^3.1.9"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = "^23.1.0" black = "^23.1.0"
@ -85,6 +86,7 @@ pytest-cov = "^4.0.0"
pandas-stubs = "^2.0.0.230412" pandas-stubs = "^2.0.0.230412"
types-pillow = "^9.5.0.2" types-pillow = "^9.5.0.2"
types-appdirs = "^1.4.3.5" types-appdirs = "^1.4.3.5"
types-pyyaml = "^6.0.12.8"
[tool.poetry.extras] [tool.poetry.extras]

View file

@ -1,6 +1,5 @@
import sys import sys
import time import time
from fastapi import FastAPI
import httpx import httpx
from multiprocess import Process, cpu_count # type: ignore from multiprocess import Process, cpu_count # type: ignore
import platform import platform
@ -11,9 +10,7 @@ from rich.panel import Panel
from rich import box from rich import box
from rich import print as rprint from rich import print as rprint
import typer import typer
from fastapi.staticfiles import StaticFiles from langflow.main import setup_app
from fastapi.responses import FileResponse
from langflow.main import create_app
from langflow.settings import settings from langflow.settings import settings
from langflow.utils.logger import configure, logger from langflow.utils.logger import configure, logger
import webbrowser import webbrowser
@ -144,15 +141,9 @@ def serve(
remove_api_keys=remove_api_keys, remove_api_keys=remove_api_keys,
cache=cache, cache=cache,
) )
# get the directory of the current file # create path object if path is provided
if not path: static_files_dir: Optional[Path] = Path(path) if path else None
frontend_path = Path(__file__).parent app = setup_app(static_files_dir=static_files_dir)
static_files_dir = frontend_path / "frontend"
else:
static_files_dir = Path(path)
app = create_app()
setup_static_files(app, static_files_dir)
# check if port is being used # check if port is being used
if is_port_in_use(port, host): if is_port_in_use(port, host):
port = get_free_port(port) port = get_free_port(port)
@ -200,29 +191,6 @@ def run_on_windows(host, port, log_level, options, app):
run_langflow(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"): def is_port_in_use(port, host="localhost"):
""" """
Check if a port is in use. Check if a port is in use.
@ -303,7 +271,7 @@ def run_langflow(host, port, log_level, options, app):
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
except Exception as e: except Exception as e:
logger.error(e) logger.exception(e)
sys.exit(1) sys.exit(1)

View file

@ -1,12 +1,6 @@
from fastapi import ( from fastapi import APIRouter, HTTPException, WebSocket, WebSocketException, status
APIRouter,
HTTPException,
WebSocket,
WebSocketException,
status,
)
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from langflow.api.v1.schemas import BuiltResponse, InitResponse, StreamData from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
from langflow.chat.manager import ChatManager from langflow.chat.manager import ChatManager
from langflow.graph.graph.base import Graph from langflow.graph.graph.base import Graph
@ -25,22 +19,39 @@ async def chat(client_id: str, websocket: WebSocket):
if client_id in chat_manager.in_memory_cache: if client_id in chat_manager.in_memory_cache:
await chat_manager.handle_websocket(client_id, websocket) await chat_manager.handle_websocket(client_id, websocket)
else: else:
# We accept the connection but close it immediately
# if the flow is not built yet
await websocket.accept()
message = "Please, build the flow before sending messages" message = "Please, build the flow before sending messages"
await websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason=message) await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=message)
except WebSocketException as exc: except WebSocketException as exc:
logger.error(exc) logger.error(exc)
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc)) await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
@router.post("/build/init", response_model=InitResponse, status_code=201) @router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
async def init_build(graph_data: dict): async def init_build(graph_data: dict, flow_id: str):
"""Initialize the build by storing graph data and returning a unique session ID.""" """Initialize the build by storing graph data and returning a unique session ID."""
try: try:
flow_id = graph_data.get("id")
if flow_id is None: if flow_id is None:
raise ValueError("No ID provided") raise ValueError("No ID provided")
flow_data_store[flow_id] = graph_data # Check if already building
if (
flow_id in flow_data_store
and flow_data_store[flow_id]["status"] == BuildStatus.IN_PROGRESS
):
return InitResponse(flowId=flow_id)
# Delete from cache if already exists
if flow_id in chat_manager.in_memory_cache:
with chat_manager.in_memory_cache._lock:
chat_manager.in_memory_cache.delete(flow_id)
logger.debug(f"Deleted flow {flow_id} from cache")
flow_data_store[flow_id] = {
"graph_data": graph_data,
"status": BuildStatus.STARTED,
}
return InitResponse(flowId=flow_id) return InitResponse(flowId=flow_id)
except Exception as exc: except Exception as exc:
@ -52,8 +63,9 @@ async def init_build(graph_data: dict):
async def build_status(flow_id: str): async def build_status(flow_id: str):
"""Check the flow_id is in the flow_data_store.""" """Check the flow_id is in the flow_data_store."""
try: try:
built = flow_id in flow_data_store and not isinstance( built = (
flow_data_store[flow_id], dict flow_id in flow_data_store
and flow_data_store[flow_id]["status"] == BuildStatus.SUCCESS
) )
return BuiltResponse( return BuiltResponse(
@ -77,7 +89,12 @@ async def stream_build(flow_id: str):
yield str(StreamData(event="error", data={"error": error_message})) yield str(StreamData(event="error", data={"error": error_message}))
return return
graph_data = flow_data_store[flow_id].get("data") if flow_data_store[flow_id].get("status") == BuildStatus.IN_PROGRESS:
error_message = "Already building"
yield str(StreamData(event="error", data={"error": error_message}))
return
graph_data = flow_data_store[flow_id].get("graph_data")
if not graph_data: if not graph_data:
error_message = "No data provided" error_message = "No data provided"
@ -95,6 +112,7 @@ async def stream_build(flow_id: str):
return return
number_of_nodes = len(graph.nodes) number_of_nodes = len(graph.nodes)
flow_data_store[flow_id]["status"] = BuildStatus.IN_PROGRESS
for i, vertex in enumerate(graph.generator_build(), 1): for i, vertex in enumerate(graph.generator_build(), 1):
try: try:
log_dict = { log_dict = {
@ -110,6 +128,7 @@ async def stream_build(flow_id: str):
except Exception as exc: except Exception as exc:
params = str(exc) params = str(exc)
valid = False valid = False
flow_data_store[flow_id]["status"] = BuildStatus.FAILURE
response = { response = {
"valid": valid, "valid": valid,
@ -121,8 +140,10 @@ async def stream_build(flow_id: str):
yield str(StreamData(event="message", data=response)) yield str(StreamData(event="message", data=response))
chat_manager.set_cache(flow_id, graph.build()) chat_manager.set_cache(flow_id, graph.build())
flow_data_store[flow_id]["status"] = BuildStatus.SUCCESS
except Exception as exc: except Exception as exc:
logger.error("Error while building the flow: %s", exc) logger.error("Error while building the flow: %s", exc)
flow_data_store[flow_id]["status"] = BuildStatus.FAILURE
yield str(StreamData(event="error", data={"error": str(exc)})) yield str(StreamData(event="error", data={"error": str(exc)}))
finally: finally:
yield str(StreamData(event="message", data=final_response)) yield str(StreamData(event="message", data=final_response))

View file

@ -11,7 +11,7 @@ from langflow.api.v1.schemas import (
UploadFileResponse, UploadFileResponse,
) )
from langflow.interface.types import build_langchain_types_dict from langflow.interface.types import langchain_types_dict
from langflow.database.base import get_session from langflow.database.base import get_session
from sqlmodel import Session from sqlmodel import Session
@ -21,7 +21,7 @@ router = APIRouter(tags=["Base"])
@router.get("/all") @router.get("/all")
def get_all(): def get_all():
return build_langchain_types_dict() return langchain_types_dict
# For backwards compatibility we will keep the old endpoint # For backwards compatibility we will keep the old endpoint

View file

@ -1,3 +1,4 @@
from enum import Enum
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
from langflow.database.models.flow import FlowCreate, FlowRead from langflow.database.models.flow import FlowCreate, FlowRead
@ -5,6 +6,15 @@ from pydantic import BaseModel, Field, validator
import json import json
class BuildStatus(Enum):
"""Status of the build."""
SUCCESS = "success"
FAILURE = "failure"
STARTED = "started"
IN_PROGRESS = "in_progress"
class GraphData(BaseModel): class GraphData(BaseModel):
"""Data inside the exported flow.""" """Data inside the exported flow."""

View file

@ -0,0 +1,2 @@
class ChatConfig:
streaming: bool = True

View file

@ -1,141 +1,275 @@
---
agents: agents:
- ZeroShotAgent ZeroShotAgent:
- JsonAgent documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
- CSVAgent JsonAgent:
- AgentInitializer documentation: "https://python.langchain.com/docs/modules/agents/toolkits/openapi"
- VectorStoreAgent CSVAgent:
- VectorStoreRouterAgent documentation: "https://python.langchain.com/docs/modules/agents/toolkits/csv"
- SQLAgent AgentInitializer:
documentation: "https://python.langchain.com/docs/modules/agents/agent_types/"
VectorStoreAgent:
documentation: ""
VectorStoreRouterAgent:
documentation: ""
SQLAgent:
documentation: ""
chains: chains:
- LLMChain LLMChain:
- LLMMathChain documentation: "https://python.langchain.com/docs/modules/chains/foundational/llm_chain"
- LLMCheckerChain LLMMathChain:
- ConversationChain documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_math"
- SeriesCharacterChain LLMCheckerChain:
- MidJourneyPromptChain documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_checker"
- TimeTravelGuideChain ConversationChain:
- SQLDatabaseChain documentation: ""
- RetrievalQA SeriesCharacterChain:
- RetrievalQAWithSourcesChain documentation: ""
- ConversationalRetrievalChain MidJourneyPromptChain:
- CombineDocsChain documentation: ""
TimeTravelGuideChain:
documentation: ""
SQLDatabaseChain:
documentation: ""
RetrievalQA:
documentation: "https://python.langchain.com/docs/modules/chains/popular/vector_db_qa"
RetrievalQAWithSourcesChain:
documentation: ""
ConversationalRetrievalChain:
documentation: "https://python.langchain.com/docs/modules/chains/popular/chat_vector_db"
CombineDocsChain:
documentation: ""
documentloaders: documentloaders:
- AirbyteJSONLoader AirbyteJSONLoader:
- CoNLLULoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/airbyte_json"
- CSVLoader CoNLLULoader:
- UnstructuredEmailLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/conll-u"
- EverNoteLoader CSVLoader:
- FacebookChatLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/csv"
- GutenbergLoader UnstructuredEmailLoader:
- BSHTMLLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/email"
- UnstructuredHTMLLoader EverNoteLoader:
# - UnstructuredImageLoader # Issue with Python 3.11 (https://github.com/Unstructured-IO/unstructured-inference/issues/83) documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/evernote"
- UnstructuredMarkdownLoader FacebookChatLoader:
- PyPDFLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/facebook_chat"
- UnstructuredPowerPointLoader GutenbergLoader:
- SRTLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gutenberg"
- TelegramChatLoader BSHTMLLoader:
- TextLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
- UnstructuredWordDocumentLoader UnstructuredHTMLLoader:
- WebBaseLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
- AZLyricsLoader UnstructuredMarkdownLoader:
- CollegeConfidentialLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/markdown"
- HNLoader PyPDFDirectoryLoader:
- IFixitLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/pdf"
- IMSDbLoader PyPDFLoader:
- GitbookLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/pdf"
- ReadTheDocsLoader UnstructuredPowerPointLoader:
- SlackDirectoryLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_powerpoint"
- NotionDirectoryLoader SRTLoader:
- DirectoryLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/subtitle"
- GitLoader 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: embeddings:
- OpenAIEmbeddings OpenAIEmbeddings:
- HuggingFaceEmbeddings documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai"
- CohereEmbeddings HuggingFaceEmbeddings:
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers"
CohereEmbeddings:
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/cohere"
llms: llms:
- OpenAI OpenAI:
# - AzureOpenAI documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/openai"
# - AzureChatOpenAI ChatOpenAI:
- ChatOpenAI documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai"
- LlamaCpp LlamaCpp:
- CTransformers documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
- Cohere CTransformers:
- Anthropic documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
- ChatAnthropic Cohere:
- HuggingFaceHub documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
Anthropic:
documentation: ""
ChatAnthropic:
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
HuggingFaceHub:
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/huggingface_hub"
VertexAI:
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/google_vertex_ai_palm"
###
# There's a bug in this component deactivating until we get it sorted: _language_models.py", line 804, in send_message
# is_blocked=safety_attributes.get("blocked", False),
# AttributeError: 'list' object has no attribute 'get'
# ChatVertexAI:
# documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/google_vertex_ai_palm"
###
memories: memories:
- ConversationBufferMemory # https://github.com/supabase-community/supabase-py/issues/482
- ConversationSummaryMemory # ZepChatMessageHistory:
- ConversationKGMemory # documentation: "https://python.langchain.com/docs/modules/memory/integrations/zep_memory"
ConversationEntityMemory:
documentation: "https://python.langchain.com/docs/modules/memory/integrations/entity_memory_with_sqlite"
# https://github.com/hwchase17/langchain/issues/6091
# SQLiteEntityStore:
# documentation: "https://python.langchain.com/docs/modules/memory/integrations/entity_memory_with_sqlite"
PostgresChatMessageHistory:
documentation: "https://python.langchain.com/docs/modules/memory/integrations/postgres_chat_message_history"
ConversationBufferMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/buffer"
ConversationSummaryMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
ConversationKGMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/kg"
ConversationBufferWindowMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/buffer_window"
VectorStoreRetrieverMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/vectorstore_retriever_memory"
prompts: prompts:
- PromptTemplate PromptTemplate:
- FewShotPromptTemplate documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
- ZeroShotPrompt ZeroShotPrompt:
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
textsplitters: textsplitters:
- CharacterTextSplitter CharacterTextSplitter:
- RecursiveCharacterTextSplitter documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
# - LatexTextSplitter RecursiveCharacterTextSplitter:
# - PythonCodeTextSplitter documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter"
toolkits: toolkits:
- OpenAPIToolkit OpenAPIToolkit:
- JsonToolkit documentation: ""
- VectorStoreInfo JsonToolkit:
- VectorStoreRouterToolkit documentation: ""
- VectorStoreToolkit VectorStoreInfo:
documentation: ""
VectorStoreRouterToolkit:
documentation: ""
VectorStoreToolkit:
documentation: ""
tools: tools:
- Search Search:
- PAL-MATH documentation: ""
- Calculator PAL-MATH:
- Serper Search documentation: ""
- Tool Calculator:
- PythonFunctionTool documentation: ""
- PythonFunction Serper Search:
- JsonSpec documentation: ""
- News API Tool:
- TMDB API documentation: ""
- Podcast API PythonFunctionTool:
- QuerySQLDataBaseTool documentation: ""
- InfoSQLDatabaseTool PythonFunction:
- ListSQLDatabaseTool documentation: ""
# - QueryCheckerTool JsonSpec:
- BingSearchRun documentation: ""
- GoogleSearchRun News API:
- GoogleSearchResults documentation: ""
- GoogleSerperRun TMDB API:
- JsonListKeysTool documentation: ""
- JsonGetValueTool Podcast API:
- PythonREPLTool documentation: ""
- PythonAstREPLTool QuerySQLDataBaseTool:
- RequestsGetTool documentation: ""
- RequestsPostTool InfoSQLDatabaseTool:
- RequestsPatchTool documentation: ""
- RequestsPutTool ListSQLDatabaseTool:
- RequestsDeleteTool documentation: ""
- WikipediaQueryRun BingSearchRun:
- WolframAlphaQueryRun documentation: ""
GoogleSearchRun:
documentation: ""
GoogleSearchResults:
documentation: ""
GoogleSerperRun:
documentation: ""
JsonListKeysTool:
documentation: ""
JsonGetValueTool:
documentation: ""
PythonREPLTool:
documentation: ""
PythonAstREPLTool:
documentation: ""
RequestsGetTool:
documentation: ""
RequestsPostTool:
documentation: ""
RequestsPatchTool:
documentation: ""
RequestsPutTool:
documentation: ""
RequestsDeleteTool:
documentation: ""
WikipediaQueryRun:
documentation: ""
WolframAlphaQueryRun:
documentation: ""
utilities: utilities:
- BingSearchAPIWrapper BingSearchAPIWrapper:
- GoogleSearchAPIWrapper documentation: ""
- GoogleSerperAPIWrapper GoogleSearchAPIWrapper:
- SearxResults documentation: ""
- SearxSearchWrapper GoogleSerperAPIWrapper:
- SerpAPIWrapper documentation: ""
- WikipediaAPIWrapper SearxResults:
- WolframAlphaAPIWrapper documentation: ""
# - ZapierNLAWrapper SearxSearchWrapper:
- SQLDatabase documentation: ""
SerpAPIWrapper:
documentation: ""
WikipediaAPIWrapper:
documentation: ""
WolframAlphaAPIWrapper:
documentation: ""
retrievers:
MultiQueryRetriever:
documentation: "https://python.langchain.com/docs/modules/data_connection/retrievers/how_to/MultiQueryRetriever"
# https://github.com/supabase-community/supabase-py/issues/482
# ZepRetriever:
# documentation: "https://python.langchain.com/docs/modules/data_connection/retrievers/integrations/zep_memorystore"
vectorstores: vectorstores:
- Chroma Chroma:
- Qdrant documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
- Weaviate Qdrant:
- FAISS documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
- Pinecone Weaviate:
- SupabaseVectorStore documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/weaviate"
- MongoDBAtlasVectorSearch FAISS:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
Pinecone:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone"
SupabaseVectorStore:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/supabase"
MongoDBAtlasVectorSearch:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/mongodb_atlas_vector_search"
wrappers: wrappers:
- RequestsWrapper RequestsWrapper:
# - ChatPromptTemplate documentation: ""
# - SystemMessagePromptTemplate
# - HumanMessagePromptTemplate

View file

@ -21,6 +21,9 @@ CUSTOM_NODES = {
"utilities": { "utilities": {
"SQLDatabase": frontend_node.agents.SQLDatabaseNode(), "SQLDatabase": frontend_node.agents.SQLDatabaseNode(),
}, },
"memories": {
"PostgresChatMessageHistory": frontend_node.memories.PostgresChatMessageHistoryFrontendNode(),
},
"chains": { "chains": {
"SeriesCharacterChain": frontend_node.chains.SeriesCharacterChainNode(), "SeriesCharacterChain": frontend_node.chains.SeriesCharacterChainNode(),
"TimeTravelGuideChain": frontend_node.chains.TimeTravelGuideChainNode(), "TimeTravelGuideChain": frontend_node.chains.TimeTravelGuideChainNode(),

View file

@ -1,6 +1,6 @@
from langflow.settings import settings from langflow.settings import settings
from sqlmodel import SQLModel, Session, create_engine from sqlmodel import SQLModel, Session, create_engine
from langflow.utils.logger import logger
if settings.database_url.startswith("sqlite"): if settings.database_url.startswith("sqlite"):
connect_args = {"check_same_thread": False} connect_args = {"check_same_thread": False}
@ -10,7 +10,9 @@ engine = create_engine(settings.database_url, connect_args=connect_args)
def create_db_and_tables(): def create_db_and_tables():
logger.debug("Creating database and tables")
SQLModel.metadata.create_all(engine) SQLModel.metadata.create_all(engine)
logger.debug("Database and tables created")
def get_session(): def get_session():

View file

@ -14,6 +14,7 @@ from langflow.graph.vertex.types import (
ToolkitVertex, ToolkitVertex,
VectorStoreVertex, VectorStoreVertex,
WrapperVertex, WrapperVertex,
RetrieverVertex,
) )
__all__ = [ __all__ = [
@ -32,4 +33,5 @@ __all__ = [
"ToolkitVertex", "ToolkitVertex",
"VectorStoreVertex", "VectorStoreVertex",
"WrapperVertex", "WrapperVertex",
"RetrieverVertex",
] ]

View file

@ -1,18 +1,5 @@
from langflow.graph.vertex.base import Vertex from langflow.graph.vertex.base import Vertex
from langflow.graph.vertex.types import ( from langflow.graph.vertex import types
AgentVertex,
ChainVertex,
DocumentLoaderVertex,
EmbeddingVertex,
LLMVertex,
MemoryVertex,
PromptVertex,
TextSplitterVertex,
ToolVertex,
ToolkitVertex,
VectorStoreVertex,
WrapperVertex,
)
from langflow.interface.agents.base import agent_creator from langflow.interface.agents.base import agent_creator
from langflow.interface.chains.base import chain_creator from langflow.interface.chains.base import chain_creator
from langflow.interface.document_loaders.base import documentloader_creator from langflow.interface.document_loaders.base import documentloader_creator
@ -25,22 +12,23 @@ from langflow.interface.toolkits.base import toolkits_creator
from langflow.interface.tools.base import tool_creator from langflow.interface.tools.base import tool_creator
from langflow.interface.vector_store.base import vectorstore_creator from langflow.interface.vector_store.base import vectorstore_creator
from langflow.interface.wrappers.base import wrapper_creator from langflow.interface.wrappers.base import wrapper_creator
from langflow.interface.retrievers.base import retriever_creator
from typing import Dict, Type from typing import Dict, Type
VERTEX_TYPE_MAP: Dict[str, Type[Vertex]] = { VERTEX_TYPE_MAP: Dict[str, Type[Vertex]] = {
**{t: PromptVertex for t in prompt_creator.to_list()}, **{t: types.PromptVertex for t in prompt_creator.to_list()},
**{t: AgentVertex for t in agent_creator.to_list()}, **{t: types.AgentVertex for t in agent_creator.to_list()},
**{t: ChainVertex for t in chain_creator.to_list()}, **{t: types.ChainVertex for t in chain_creator.to_list()},
**{t: ToolVertex for t in tool_creator.to_list()}, **{t: types.ToolVertex for t in tool_creator.to_list()},
**{t: ToolkitVertex for t in toolkits_creator.to_list()}, **{t: types.ToolkitVertex for t in toolkits_creator.to_list()},
**{t: WrapperVertex for t in wrapper_creator.to_list()}, **{t: types.WrapperVertex for t in wrapper_creator.to_list()},
**{t: LLMVertex for t in llm_creator.to_list()}, **{t: types.LLMVertex for t in llm_creator.to_list()},
**{t: MemoryVertex for t in memory_creator.to_list()}, **{t: types.MemoryVertex for t in memory_creator.to_list()},
**{t: EmbeddingVertex for t in embedding_creator.to_list()}, **{t: types.EmbeddingVertex for t in embedding_creator.to_list()},
**{t: VectorStoreVertex for t in vectorstore_creator.to_list()}, **{t: types.VectorStoreVertex for t in vectorstore_creator.to_list()},
**{t: DocumentLoaderVertex for t in documentloader_creator.to_list()}, **{t: types.DocumentLoaderVertex for t in documentloader_creator.to_list()},
**{t: TextSplitterVertex for t in textsplitter_creator.to_list()}, **{t: types.TextSplitterVertex for t in textsplitter_creator.to_list()},
**{t: types.RetrieverVertex for t in retriever_creator.to_list()},
} }

View file

@ -112,6 +112,11 @@ class MemoryVertex(Vertex):
super().__init__(data, base_type="memory") super().__init__(data, base_type="memory")
class RetrieverVertex(Vertex):
def __init__(self, data: Dict):
super().__init__(data, base_type="retrievers")
class TextSplitterVertex(Vertex): class TextSplitterVertex(Vertex):
def __init__(self, data: Dict): def __init__(self, data: Dict):
super().__init__(data, base_type="textsplitters") super().__init__(data, base_type="textsplitters")

View file

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

View file

@ -10,8 +10,12 @@ from langchain import (
text_splitter, text_splitter,
) )
from langchain.agents import agent_toolkits from langchain.agents import agent_toolkits
from langchain.chat_models import AzureChatOpenAI, ChatOpenAI from langchain.chat_models import (
from langchain.chat_models import ChatAnthropic AzureChatOpenAI,
ChatOpenAI,
ChatVertexAI,
ChatAnthropic,
)
from langflow.interface.importing.utils import import_class from langflow.interface.importing.utils import import_class
from langflow.interface.agents.custom import CUSTOM_AGENTS from langflow.interface.agents.custom import CUSTOM_AGENTS
@ -22,6 +26,7 @@ llm_type_to_cls_dict = llms.type_to_cls_dict
llm_type_to_cls_dict["anthropic-chat"] = ChatAnthropic # type: ignore llm_type_to_cls_dict["anthropic-chat"] = ChatAnthropic # type: ignore
llm_type_to_cls_dict["azure-chat"] = AzureChatOpenAI # type: ignore llm_type_to_cls_dict["azure-chat"] = AzureChatOpenAI # type: ignore
llm_type_to_cls_dict["openai-chat"] = ChatOpenAI # type: ignore llm_type_to_cls_dict["openai-chat"] = ChatOpenAI # type: ignore
llm_type_to_cls_dict["vertexai-chat"] = ChatVertexAI # type: ignore
# Toolkits # Toolkits

View file

@ -44,6 +44,7 @@ def import_by_type(_type: str, name: str) -> Any:
"documentloaders": import_documentloader, "documentloaders": import_documentloader,
"textsplitters": import_textsplitter, "textsplitters": import_textsplitter,
"utilities": import_utility, "utilities": import_utility,
"retrievers": import_retriever,
} }
if _type == "llms": if _type == "llms":
key = "chat" if "chat" in name.lower() else "llm" key = "chat" if "chat" in name.lower() else "llm"
@ -59,6 +60,11 @@ def import_chat_llm(llm: str) -> BaseChatModel:
return import_class(f"langchain.chat_models.{llm}") return import_class(f"langchain.chat_models.{llm}")
def import_retriever(retriever: str) -> Any:
"""Import retriever from retriever name"""
return import_module(f"from langchain.retrievers import {retriever}")
def import_memory(memory: str) -> Any: def import_memory(memory: str) -> Any:
"""Import memory from memory name""" """Import memory from memory name"""
return import_module(f"from langchain.memory import {memory}") return import_module(f"from langchain.memory import {memory}")

View file

@ -0,0 +1,9 @@
def initialize_vertexai(class_object, params):
if credentials_path := params.get("credentials"):
from google.oauth2 import service_account # type: ignore
credentials_object = service_account.Credentials.from_service_account_file(
filename=credentials_path
)
params["credentials"] = credentials_object
return class_object(**params)

View file

@ -1,11 +1,13 @@
import json import json
from typing import Any, Callable, Dict, Sequence from typing import Any, Callable, Dict, Sequence, Type
from langchain.agents import ZeroShotAgent from langchain.agents import ZeroShotAgent
from langchain.agents import agent as agent_module from langchain.agents import agent as agent_module
from langchain.agents.agent import AgentExecutor from langchain.agents.agent import AgentExecutor
from langchain.agents.agent_toolkits.base import BaseToolkit from langchain.agents.agent_toolkits.base import BaseToolkit
from langchain.agents.tools import BaseTool from langchain.agents.tools import BaseTool
from langflow.interface.initialize.llm import initialize_vertexai
from langflow.interface.initialize.vector_store import vecstore_initializer from langflow.interface.initialize.vector_store import vecstore_initializer
from pydantic import ValidationError from pydantic import ValidationError
@ -14,8 +16,13 @@ from langflow.interface.custom_lists import CUSTOM_NODES
from langflow.interface.importing.utils import get_function, import_by_type from langflow.interface.importing.utils import get_function, import_by_type
from langflow.interface.toolkits.base import toolkits_creator from langflow.interface.toolkits.base import toolkits_creator
from langflow.interface.chains.base import chain_creator from langflow.interface.chains.base import chain_creator
from langflow.interface.retrievers.base import retriever_creator
from langflow.interface.utils import load_file_into_dict from langflow.interface.utils import load_file_into_dict
from langflow.utils import validate from langflow.utils import validate
from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore
from langchain.document_loaders.base import BaseLoader
from langchain.prompts.base import BasePromptTemplate
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any: def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
@ -43,8 +50,8 @@ def convert_params_to_sets(params):
def convert_kwargs(params): def convert_kwargs(params):
# if *kwargs are passed as a string, convert to dict # if *kwargs are passed as a string, convert to dict
# first find any key that has kwargs in it # first find any key that has kwargs or config in it
kwargs_keys = [key for key in params.keys() if "kwargs" in key] kwargs_keys = [key for key in params.keys() if "kwargs" in key or "config" in key]
for key in kwargs_keys: for key in kwargs_keys:
if isinstance(params[key], str): if isinstance(params[key], str):
params[key] = json.loads(params[key]) params[key] = json.loads(params[key])
@ -72,11 +79,57 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
return instantiate_utility(node_type, class_object, params) return instantiate_utility(node_type, class_object, params)
elif base_type == "chains": elif base_type == "chains":
return instantiate_chains(node_type, class_object, params) return instantiate_chains(node_type, class_object, params)
elif base_type == "llms":
return instantiate_llm(node_type, class_object, params)
elif base_type == "retrievers":
return instantiate_retriever(node_type, class_object, params)
elif base_type == "memory":
return instantiate_memory(node_type, class_object, params)
else: else:
return class_object(**params) return class_object(**params)
def instantiate_chains(node_type, class_object, params): def instantiate_llm(node_type, class_object, params: Dict):
# This is a workaround so JinaChat works until streaming is implemented
# if "openai_api_base" in params and "jina" in params["openai_api_base"]:
# False if condition is True
if node_type == "VertexAI":
return initialize_vertexai(class_object=class_object, params=params)
return class_object(**params)
def instantiate_memory(node_type, class_object, params):
try:
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
params["retriever"] = params["retriever"].as_retriever()
return class_object(**params)
# I want to catch a specific attribute error that happens
# when the object does not have a cursor attribute
except Exception as exc:
if "object has no attribute 'cursor'" in str(
exc
) or 'object has no field "conn"' in str(exc):
raise AttributeError(
(
"Failed to build connection to database."
f" Please check your connection string and try again. Error: {exc}"
)
) from exc
raise exc
def instantiate_retriever(node_type, class_object, params):
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
params["retriever"] = params["retriever"].as_retriever()
if node_type in retriever_creator.from_method_nodes:
method = retriever_creator.from_method_nodes[node_type]
if class_method := getattr(class_object, method, None):
return class_method(**params)
raise ValueError(f"Method {method} not found in {class_object}")
return class_object(**params)
def instantiate_chains(node_type, class_object: Type[Chain], params: Dict):
if "retriever" in params and hasattr(params["retriever"], "as_retriever"): if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
params["retriever"] = params["retriever"].as_retriever() params["retriever"] = params["retriever"].as_retriever()
if node_type in chain_creator.from_method_nodes: if node_type in chain_creator.from_method_nodes:
@ -88,11 +141,11 @@ def instantiate_chains(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_agent(class_object, params): def instantiate_agent(class_object: Type[agent_module.Agent], params: Dict):
return load_agent_executor(class_object, params) return load_agent_executor(class_object, params)
def instantiate_prompt(node_type, class_object, params): def instantiate_prompt(node_type, class_object: Type[BasePromptTemplate], params: Dict):
if node_type == "ZeroShotPrompt": if node_type == "ZeroShotPrompt":
if "tools" not in params: if "tools" not in params:
params["tools"] = [] params["tools"] = []
@ -100,7 +153,7 @@ def instantiate_prompt(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_tool(node_type, class_object, params): def instantiate_tool(node_type, class_object: Type[BaseTool], params: Dict):
if node_type == "JsonSpec": if node_type == "JsonSpec":
params["dict_"] = load_file_into_dict(params.pop("path")) params["dict_"] = load_file_into_dict(params.pop("path"))
return class_object(**params) return class_object(**params)
@ -118,7 +171,7 @@ def instantiate_tool(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_toolkit(node_type, class_object, params): def instantiate_toolkit(node_type, class_object: Type[BaseToolkit], params: Dict):
loaded_toolkit = class_object(**params) loaded_toolkit = class_object(**params)
# Commenting this out for now to use toolkits as normal tools # Commenting this out for now to use toolkits as normal tools
# if toolkits_creator.has_create_function(node_type): # if toolkits_creator.has_create_function(node_type):
@ -128,7 +181,7 @@ def instantiate_toolkit(node_type, class_object, params):
return loaded_toolkit return loaded_toolkit
def instantiate_embedding(class_object, params): def instantiate_embedding(class_object, params: Dict):
params.pop("model", None) params.pop("model", None)
params.pop("headers", None) params.pop("headers", None)
try: try:
@ -142,7 +195,7 @@ def instantiate_embedding(class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_vectorstore(class_object, params): def instantiate_vectorstore(class_object: Type[VectorStore], params: Dict):
search_kwargs = params.pop("search_kwargs", {}) search_kwargs = params.pop("search_kwargs", {})
if initializer := vecstore_initializer.get(class_object.__name__): if initializer := vecstore_initializer.get(class_object.__name__):
vecstore = initializer(class_object, params) vecstore = initializer(class_object, params)
@ -158,7 +211,7 @@ def instantiate_vectorstore(class_object, params):
return vecstore return vecstore
def instantiate_documentloader(class_object, params): def instantiate_documentloader(class_object: Type[BaseLoader], params: Dict):
if "file_filter" in params: if "file_filter" in params:
# file_filter will be a string but we need a function # file_filter will be a string but we need a function
# that will be used to filter the files using file_filter # that will be used to filter the files using file_filter
@ -171,35 +224,55 @@ def instantiate_documentloader(class_object, params):
extension.strip() in x for extension in extensions extension.strip() in x for extension in extensions
) )
metadata = params.pop("metadata", None) 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() docs = class_object(**params).load()
# Now if metadata is an empty dict, we will not add it to the documents
if metadata: if metadata:
if 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
for doc in docs: for doc in docs:
doc.metadata = metadata # 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 return docs
def instantiate_textsplitter(class_object, params): def instantiate_textsplitter(
class_object,
params: Dict,
):
try: try:
documents = params.pop("documents") documents = params.pop("documents")
except KeyError as e: except KeyError as exc:
raise ValueError( raise ValueError(
"The source you provided did not load correctly or was empty." "The source you provided did not load correctly or was empty."
"Try changing the chunk_size of the Text Splitter." "Try changing the chunk_size of the Text Splitter."
) from e ) from exc
text_splitter = class_object(**params)
if (
"separator_type" in params and params["separator_type"] == "Text"
) or "separator_type" not in params:
params.pop("separator_type", None)
text_splitter = class_object(**params)
else:
from langchain.text_splitter import Language
language = params.pop("separator_type", None)
params["language"] = Language(language)
params.pop("separators", None)
text_splitter = class_object.from_language(**params)
return text_splitter.split_documents(documents) return text_splitter.split_documents(documents)
def instantiate_utility(node_type, class_object, params): def instantiate_utility(node_type, class_object, params: Dict):
if node_type == "SQLDatabase": if node_type == "SQLDatabase":
return class_object.from_uri(params.pop("uri")) return class_object.from_uri(params.pop("uri"))
return class_object(**params) return class_object(**params)
@ -225,6 +298,8 @@ def load_agent_executor(agent_class: type[agent_module.Agent], params, **kwargs)
"""Load agent executor from agent class, tools and chain""" """Load agent executor from agent class, tools and chain"""
allowed_tools: Sequence[BaseTool] = params.get("allowed_tools", []) allowed_tools: Sequence[BaseTool] = params.get("allowed_tools", [])
llm_chain = params["llm_chain"] llm_chain = params["llm_chain"]
# agent has hidden args for memory. might need to be support
# memory = params["memory"]
# if allowed_tools is not a list or set, make it a list # if allowed_tools is not a list or set, make it a list
if not isinstance(allowed_tools, (list, set)) and isinstance( if not isinstance(allowed_tools, (list, set)) and isinstance(
allowed_tools, BaseTool allowed_tools, BaseTool
@ -237,6 +312,7 @@ def load_agent_executor(agent_class: type[agent_module.Agent], params, **kwargs)
return AgentExecutor.from_agent_and_tools( return AgentExecutor.from_agent_and_tools(
agent=agent, agent=agent,
tools=allowed_tools, tools=allowed_tools,
# memory=memory,
**kwargs, **kwargs,
) )

View file

@ -11,6 +11,7 @@ from langflow.interface.tools.base import tool_creator
from langflow.interface.utilities.base import utility_creator from langflow.interface.utilities.base import utility_creator
from langflow.interface.vector_store.base import vectorstore_creator from langflow.interface.vector_store.base import vectorstore_creator
from langflow.interface.wrappers.base import wrapper_creator from langflow.interface.wrappers.base import wrapper_creator
from langflow.interface.retrievers.base import retriever_creator
def get_type_dict(): def get_type_dict():
@ -28,6 +29,7 @@ def get_type_dict():
"embeddings": embedding_creator.to_list(), "embeddings": embedding_creator.to_list(),
"textSplitters": textsplitter_creator.to_list(), "textSplitters": textsplitter_creator.to_list(),
"utilities": utility_creator.to_list(), "utilities": utility_creator.to_list(),
"retrievers": retriever_creator.to_list(),
} }

View file

@ -6,12 +6,18 @@ from langflow.settings import settings
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.memories import MemoryFrontendNode from langflow.template.frontend_node.memories import MemoryFrontendNode
from langflow.utils.logger import logger from langflow.utils.logger import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class, build_template_from_method
from langflow.custom.customs import get_custom_nodes
class MemoryCreator(LangChainTypeCreator): class MemoryCreator(LangChainTypeCreator):
type_name: str = "memories" type_name: str = "memories"
from_method_nodes = {
"ZepChatMessageHistory": "__init__",
"SQLiteEntityStore": "__init__",
}
@property @property
def frontend_node_class(self) -> Type[FrontendNode]: def frontend_node_class(self) -> Type[FrontendNode]:
"""The class type of the FrontendNode created in frontend_node.""" """The class type of the FrontendNode created in frontend_node."""
@ -26,6 +32,14 @@ class MemoryCreator(LangChainTypeCreator):
def get_signature(self, name: str) -> Optional[Dict]: def get_signature(self, name: str) -> Optional[Dict]:
"""Get the signature of a memory.""" """Get the signature of a memory."""
try: try:
if name in get_custom_nodes(self.type_name).keys():
return get_custom_nodes(self.type_name)[name]
elif name in self.from_method_nodes:
return build_template_from_method(
name,
type_to_cls_dict=memory_type_to_cls_dict,
method_name=self.from_method_nodes[name],
)
return build_template_from_class(name, memory_type_to_cls_dict) return build_template_from_class(name, memory_type_to_cls_dict)
except ValueError as exc: except ValueError as exc:
raise ValueError("Memory not found") from exc raise ValueError("Memory not found") from exc

View file

@ -0,0 +1,58 @@
from typing import Any, Dict, List, Optional, Type
from langchain import retrievers
from langflow.interface.base import LangChainTypeCreator
from langflow.interface.importing.utils import import_class
from langflow.settings import settings
from langflow.template.frontend_node.retrievers import RetrieverFrontendNode
from langflow.utils.logger import logger
from langflow.utils.util import build_template_from_method, build_template_from_class
class RetrieverCreator(LangChainTypeCreator):
type_name: str = "retrievers"
from_method_nodes = {"MultiQueryRetriever": "from_llm", "ZepRetriever": "__init__"}
@property
def frontend_node_class(self) -> Type[RetrieverFrontendNode]:
return RetrieverFrontendNode
@property
def type_to_loader_dict(self) -> Dict:
if self.type_dict is None:
self.type_dict: dict[str, Any] = {
retriever_name: import_class(f"langchain.retrievers.{retriever_name}")
for retriever_name in retrievers.__all__
}
return self.type_dict
def get_signature(self, name: str) -> Optional[Dict]:
"""Get the signature of an embedding."""
try:
if name in self.from_method_nodes:
return build_template_from_method(
name,
type_to_cls_dict=self.type_to_loader_dict,
method_name=self.from_method_nodes[name],
)
else:
return build_template_from_class(
name, type_to_cls_dict=self.type_to_loader_dict
)
except ValueError as exc:
raise ValueError(f"Retriever {name} not found") from exc
except AttributeError as exc:
logger.error(f"Retriever {name} not loaded: {exc}")
return None
def to_list(self) -> List[str]:
return [
retriever
for retriever in self.type_to_loader_dict.keys()
if retriever in settings.retrievers or settings.dev
]
retriever_creator = RetrieverCreator()

View file

@ -62,6 +62,10 @@ def update_memory_keys(langchain_object, possible_new_mem_key):
if key not in [langchain_object.memory.memory_key, possible_new_mem_key] if key not in [langchain_object.memory.memory_key, possible_new_mem_key]
][0] ][0]
langchain_object.memory.input_key = input_key keys = [input_key, output_key, possible_new_mem_key]
langchain_object.memory.output_key = output_key attrs = ["input_key", "output_key", "memory_key"]
langchain_object.memory.memory_key = possible_new_mem_key for key, attr in zip(keys, attrs):
try:
setattr(langchain_object.memory, attr, key)
except ValueError as exc:
logger.debug(f"{langchain_object.memory} has no attribute {attr} ({exc})")

View file

@ -11,6 +11,7 @@ from langflow.interface.tools.base import tool_creator
from langflow.interface.utilities.base import utility_creator from langflow.interface.utilities.base import utility_creator
from langflow.interface.vector_store.base import vectorstore_creator from langflow.interface.vector_store.base import vectorstore_creator
from langflow.interface.wrappers.base import wrapper_creator from langflow.interface.wrappers.base import wrapper_creator
from langflow.interface.retrievers.base import retriever_creator
def get_type_list(): def get_type_list():
@ -44,6 +45,7 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union
documentloader_creator, documentloader_creator,
textsplitter_creator, textsplitter_creator,
utility_creator, utility_creator,
retriever_creator,
] ]
all_types = {} all_types = {}
@ -52,3 +54,6 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union
if created_types[creator.type_name].values(): if created_types[creator.type_name].values():
all_types.update(created_types) all_types.update(created_types)
return all_types return all_types
langchain_types_dict = build_langchain_types_dict()

View file

@ -4,10 +4,12 @@ import os
from io import BytesIO from io import BytesIO
import re import re
import yaml import yaml
from langchain.base_language import BaseLanguageModel from langchain.base_language import BaseLanguageModel
from PIL.Image import Image from PIL.Image import Image
from langflow.utils.logger import logger from langflow.utils.logger import logger
from langflow.chat.config import ChatConfig
def load_file_into_dict(file_path: str) -> dict: def load_file_into_dict(file_path: str) -> dict:
@ -49,9 +51,9 @@ def try_setting_streaming_options(langchain_object, websocket):
if isinstance(llm, BaseLanguageModel): if isinstance(llm, BaseLanguageModel):
if hasattr(llm, "streaming") and isinstance(llm.streaming, bool): 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): elif hasattr(llm, "stream") and isinstance(llm.stream, bool):
llm.stream = True llm.stream = ChatConfig.streaming
return langchain_object return langchain_object

View file

@ -1,5 +1,9 @@
from pathlib import Path
from typing import Optional
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from langflow.api import router from langflow.api import router
from langflow.database.base import create_db_and_tables from langflow.database.base import create_db_and_tables
@ -33,6 +37,42 @@ def create_app():
return app 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
app = create_app() app = create_app()

View file

@ -51,5 +51,6 @@ async def get_result_and_steps(langchain_object, message: str, **kwargs):
) )
thought = format_actions(intermediate_steps) if intermediate_steps else "" thought = format_actions(intermediate_steps) if intermediate_steps else ""
except Exception as exc: except Exception as exc:
logger.exception(exc)
raise ValueError(f"Error: {str(exc)}") from exc raise ValueError(f"Error: {str(exc)}") from exc
return result, thought return result, thought

View file

@ -107,6 +107,10 @@ def process_graph_cached(data_graph: Dict[str, Any], inputs: Optional[dict] = No
elif isinstance(langchain_object, VectorStore): elif isinstance(langchain_object, VectorStore):
class_name = langchain_object.__class__.__name__ class_name = langchain_object.__class__.__name__
result = {"message": f"Processed {class_name} successfully"} result = {"message": f"Processed {class_name} successfully"}
else:
raise ValueError(
f"Unknown langchain_object type: {type(langchain_object).__name__}"
)
return result return result

View file

@ -1,29 +1,41 @@
import os import os
from typing import List
import yaml import yaml
from pydantic import BaseSettings, root_validator from pydantic import BaseSettings, root_validator
from langflow.utils.logger import logger
class Settings(BaseSettings): class Settings(BaseSettings):
chains: List[str] = [] chains: dict = {}
agents: List[str] = [] agents: dict = {}
prompts: List[str] = [] prompts: dict = {}
llms: List[str] = [] llms: dict = {}
tools: List[str] = [] tools: dict = {}
memories: List[str] = [] memories: dict = {}
embeddings: List[str] = [] embeddings: dict = {}
vectorstores: List[str] = [] vectorstores: dict = {}
documentloaders: List[str] = [] documentloaders: dict = {}
wrappers: List[str] = [] wrappers: dict = {}
toolkits: List[str] = [] retrievers: dict = {}
textsplitters: List[str] = [] toolkits: dict = {}
utilities: List[str] = [] textsplitters: dict = {}
utilities: dict = {}
dev: bool = False dev: bool = False
database_url: str = "sqlite:///./langflow.db" database_url: str
cache: str = "InMemoryCache" cache: str = "InMemoryCache"
remove_api_keys: bool = False remove_api_keys: bool = False
@root_validator(pre=True)
def set_database_url(cls, values):
if "database_url" not in values:
logger.debug("No database_url provided, trying DATABASE_URL env variable")
if database_url := os.getenv("DATABASE_URL"):
values["database_url"] = database_url
else:
logger.debug("No DATABASE_URL env variable, using sqlite database")
values["database_url"] = "sqlite:///./langflow.db"
return values
class Config: class Config:
validate_assignment = True validate_assignment = True
extra = "ignore" extra = "ignore"
@ -38,16 +50,21 @@ class Settings(BaseSettings):
def update_from_yaml(self, file_path: str, dev: bool = False): def update_from_yaml(self, file_path: str, dev: bool = False):
new_settings = load_settings_from_yaml(file_path) new_settings = load_settings_from_yaml(file_path)
self.chains = new_settings.chains or [] self.chains = new_settings.chains or {}
self.agents = new_settings.agents or [] self.agents = new_settings.agents or {}
self.prompts = new_settings.prompts or [] self.prompts = new_settings.prompts or {}
self.llms = new_settings.llms or [] self.llms = new_settings.llms or {}
self.tools = new_settings.tools or [] self.tools = new_settings.tools or {}
self.memories = new_settings.memories or [] self.memories = new_settings.memories or {}
self.wrappers = new_settings.wrappers or [] self.wrappers = new_settings.wrappers or {}
self.toolkits = new_settings.toolkits or [] self.toolkits = new_settings.toolkits or {}
self.textsplitters = new_settings.textsplitters or [] self.textsplitters = new_settings.textsplitters or {}
self.utilities = new_settings.utilities or [] self.utilities = new_settings.utilities or {}
self.embeddings = new_settings.embeddings or {}
self.vectorstores = new_settings.vectorstores or {}
self.documentloaders = new_settings.documentloaders or {}
self.retrievers = new_settings.retrievers or {}
self.dev = dev self.dev = dev
def update_settings(self, **kwargs): def update_settings(self, **kwargs):

View file

@ -21,6 +21,7 @@ class TemplateFieldCreator(BaseModel, ABC):
name: str = "" name: str = ""
display_name: Optional[str] = None display_name: Optional[str] = None
advanced: bool = False advanced: bool = False
info: Optional[str] = ""
def to_dict(self): def to_dict(self):
result = self.dict() result = self.dict()

View file

@ -1,12 +1,43 @@
import re import re
from typing import List, Optional from typing import List, Optional
from pydantic import BaseModel from pydantic import BaseModel, Field
from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.template.base import Template from langflow.template.template.base import Template
from langflow.utils import constants from langflow.utils import constants
from langflow.template.frontend_node.formatter import field_formatters
CLASSES_TO_REMOVE = ["Serializable", "BaseModel"]
class FieldFormatters(BaseModel):
formatters = {
"openai_api_key": field_formatters.OpenAIAPIKeyFormatter(),
}
base_formatters = {
"kwargs": field_formatters.KwargsFormatter(),
"optional": field_formatters.RemoveOptionalFormatter(),
"list": field_formatters.ListTypeFormatter(),
"dict": field_formatters.DictTypeFormatter(),
"union": field_formatters.UnionTypeFormatter(),
"multiline": field_formatters.MultilineFieldFormatter(),
"show": field_formatters.ShowFieldFormatter(),
"password": field_formatters.PasswordFieldFormatter(),
"default": field_formatters.DefaultValueFormatter(),
"headers": field_formatters.HeadersDefaultValueFormatter(),
"dict_code_file": field_formatters.DictCodeFileFormatter(),
"model_fields": field_formatters.ModelSpecificFieldFormatter(),
}
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
for key, formatter in self.base_formatters.items():
formatter.format(field, name)
for key, formatter in self.formatters.items():
if key == field.name:
formatter.format(field, name)
class FrontendNode(BaseModel): class FrontendNode(BaseModel):
@ -15,14 +46,37 @@ class FrontendNode(BaseModel):
base_classes: List[str] base_classes: List[str]
name: str = "" name: str = ""
display_name: str = "" display_name: str = ""
documentation: str = ""
field_formatters: FieldFormatters = Field(default_factory=FieldFormatters)
def process_base_classes(self) -> None:
"""Removes unwanted base classes from the list of base classes."""
self.base_classes = [
base_class
for base_class in self.base_classes
if base_class not in CLASSES_TO_REMOVE
]
# field formatters is an instance attribute but it is not used in the class
# so we need to create a method to get it
@staticmethod
def get_field_formatters() -> FieldFormatters:
return FieldFormatters()
def set_documentation(self, documentation: str) -> None:
"""Sets the documentation of the frontend node."""
self.documentation = documentation
def to_dict(self) -> dict: def to_dict(self) -> dict:
"""Returns a dict representation of the frontend node."""
self.process_base_classes()
return { return {
self.name: { self.name: {
"template": self.template.to_dict(self.format_field), "template": self.template.to_dict(self.format_field),
"description": self.description, "description": self.description,
"base_classes": self.base_classes, "base_classes": self.base_classes,
"display_name": self.display_name or self.name, "display_name": self.display_name or self.name,
"documentation": self.documentation,
}, },
} }
@ -35,33 +89,8 @@ class FrontendNode(BaseModel):
@staticmethod @staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:
"""Formats a given field based on its attributes and value.""" """Formats a given field based on its attributes and value."""
SPECIAL_FIELD_HANDLERS = {
"allowed_tools": lambda field: "Tool",
"max_value_length": lambda field: "int",
}
key = field.name FrontendNode.get_field_formatters().format(field, name)
value = field.to_dict()
_type = value["type"]
_type = FrontendNode.remove_optional(_type)
_type, is_list = FrontendNode.check_for_list_type(_type)
field.is_list = is_list or field.is_list
_type = FrontendNode.replace_mapping_with_dict(_type)
_type = FrontendNode.handle_union_type(_type)
field.field_type = FrontendNode.handle_special_field(
field, key, _type, SPECIAL_FIELD_HANDLERS
)
field.field_type = FrontendNode.handle_dict_type(field, _type)
field.show = FrontendNode.should_show_field(key, field.required)
field.password = FrontendNode.should_be_password(key, field.show)
field.multiline = FrontendNode.should_be_multiline(key)
FrontendNode.replace_default_value(field, value)
FrontendNode.handle_specific_field_values(field, key, name)
FrontendNode.handle_kwargs_field(field)
FrontendNode.handle_api_key_field(field, key)
@staticmethod @staticmethod
def remove_optional(_type: str) -> str: def remove_optional(_type: str) -> str:

View file

@ -49,6 +49,10 @@ class ChainFrontendNode(FrontendNode):
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:
FrontendNode.format_field(field, name) FrontendNode.format_field(field, name)
if "name" == "RetrievalQA" and field.name == "memory":
field.show = False
field.required = False
field.advanced = False field.advanced = False
if "key" in field.name: if "key" in field.name:
field.password = False field.password = False

View file

@ -32,3 +32,29 @@ You are a good listener and you can talk about anything.
HUMAN_PROMPT = "{input}" HUMAN_PROMPT = "{input}"
QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"] QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"]
CTRANSFORMERS_DEFAULT_CONFIG = {
"top_k": 40,
"top_p": 0.95,
"temperature": 0.8,
"repetition_penalty": 1.1,
"last_n_tokens": 64,
"seed": -1,
"max_new_tokens": 256,
"stop": None,
"stream": False,
"reset": True,
"batch_size": 8,
"threads": -1,
"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.
"""

View file

@ -120,29 +120,23 @@ class DocumentLoaderFrontNode(FrontendNode):
"DirectoryLoader", "DirectoryLoader",
"ReadTheDocsLoader", "ReadTheDocsLoader",
"NotionDirectoryLoader", "NotionDirectoryLoader",
"PyPDFDirectoryLoader",
}: }:
name = "path" name = "path"
display_name = "Local directory" display_name = "Local directory"
if name: if name:
self.template.add_field(
TemplateField(
field_type="str",
required=True,
show=True,
name=name,
value="",
display_name=display_name,
)
)
if self.template.type_name in {"DirectoryLoader"}: if self.template.type_name in {"DirectoryLoader"}:
for field in build_directory_loader_fields():
self.template.add_field(field)
else:
self.template.add_field( self.template.add_field(
TemplateField( TemplateField(
field_type="str", field_type="str",
required=True, required=True,
show=True, show=True,
name="glob", name=name,
value="**/*.txt", value="",
display_name="glob", display_name=display_name,
) )
) )
# add a metadata field of type dict # add a metadata field of type dict
@ -165,3 +159,101 @@ class DocumentLoaderFrontNode(FrontendNode):
field.show = True field.show = True
field.advanced = False field.advanced = False
field.show = True field.show = True
def build_directory_loader_fields():
# if loader_kwargs is None:
# loader_kwargs = {}
# self.path = path
# self.glob = glob
# self.load_hidden = load_hidden
# self.loader_cls = loader_cls
# self.loader_kwargs = loader_kwargs
# self.silent_errors = silent_errors
# self.recursive = recursive
# self.show_progress = show_progress
# self.use_multithreading = use_multithreading
# self.max_concurrency = max_concurrency
# Based on the above fields, we can build the following fields:
# path, glob, load_hidden, silent_errors, recursive, show_progress, use_multithreading, max_concurrency
# path
path = TemplateField(
field_type="str",
required=True,
show=True,
name="path",
value="",
display_name="Local directory",
advanced=False,
)
# glob
glob = TemplateField(
field_type="str",
required=True,
show=True,
name="glob",
value="**/*.txt",
display_name="glob",
advanced=False,
)
# load_hidden
load_hidden = TemplateField(
field_type="bool",
required=False,
show=True,
name="load_hidden",
value="False",
display_name="Load hidden files",
advanced=True,
)
# silent_errors
silent_errors = TemplateField(
field_type="bool",
required=False,
show=True,
name="silent_errors",
value="False",
display_name="Silent errors",
advanced=True,
)
# recursive
recursive = TemplateField(
field_type="bool",
required=False,
show=True,
name="recursive",
value="True",
display_name="Recursive",
advanced=True,
)
# use_multithreading
use_multithreading = TemplateField(
field_type="bool",
required=False,
show=True,
name="use_multithreading",
value="True",
display_name="Use multithreading",
advanced=True,
)
# max_concurrency
max_concurrency = TemplateField(
field_type="int",
required=False,
show=True,
name="max_concurrency",
value=10,
display_name="Max concurrency",
advanced=True,
)
return (
path,
glob,
load_hidden,
silent_errors,
recursive,
use_multithreading,
max_concurrency,
)

View file

@ -0,0 +1,10 @@
from abc import ABC, abstractmethod
from typing import Optional
from langflow.template.field.base import TemplateField
class FieldFormatter(ABC):
@abstractmethod
def format(self, field: TemplateField, name: Optional[str]) -> None:
pass

View file

@ -0,0 +1,162 @@
from typing import Optional
from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS
from langflow.template.frontend_node.formatter.base import FieldFormatter
import re
from langflow.utils.constants import (
ANTHROPIC_MODELS,
CHAT_OPENAI_MODELS,
OPENAI_MODELS,
)
class OpenAIAPIKeyFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
if "api_key" in field.name and "OpenAI" in str(name):
field.display_name = "OpenAI API Key"
field.required = False
if field.value is None:
field.value = ""
class ModelSpecificFieldFormatter(FieldFormatter):
MODEL_DICT = {
"OpenAI": OPENAI_MODELS,
"ChatOpenAI": CHAT_OPENAI_MODELS,
"Anthropic": ANTHROPIC_MODELS,
"ChatAnthropic": ANTHROPIC_MODELS,
}
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
if name in self.MODEL_DICT and field.name == "model_name":
field.options = self.MODEL_DICT[name]
field.is_list = True
class KwargsFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
if "kwargs" in field.name.lower():
field.advanced = True
field.required = False
field.show = False
class APIKeyFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
if "api" in field.name.lower() and "key" in field.name.lower():
field.required = False
field.advanced = False
field.display_name = field.name.replace("_", " ").title()
field.display_name = field.display_name.replace("Api", "API")
class RemoveOptionalFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
_type = field.field_type
field.field_type = re.sub(r"Optional\[(.*)\]", r"\1", _type)
class ListTypeFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
_type = field.field_type
is_list = "List" in _type or "Sequence" in _type
if is_list:
_type = re.sub(r"(List|Sequence)\[(.*)\]", r"\2", _type)
field.is_list = True
field.field_type = _type
class DictTypeFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
_type = field.field_type
_type = _type.replace("Mapping", "dict")
field.field_type = _type
class UnionTypeFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
_type = field.field_type
if "Union" in _type:
_type = _type.replace("Union[", "")[:-1]
_type = _type.split(",")[0]
_type = _type.replace("]", "").replace("[", "")
field.field_type = _type
class SpecialFieldFormatter(FieldFormatter):
SPECIAL_FIELD_HANDLERS = {
"allowed_tools": lambda field: "Tool",
"max_value_length": lambda field: "int",
}
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
handler = self.SPECIAL_FIELD_HANDLERS.get(field.name)
field.field_type = handler(field) if handler else field.field_type
class ShowFieldFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
key = field.name
required = field.required
field.show = (
(required and key not in ["input_variables"])
or key in FORCE_SHOW_FIELDS
or "api" in key
or ("key" in key and "input" not in key and "output" not in key)
)
class PasswordFieldFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
key = field.name
show = field.show
if (
any(text in key.lower() for text in {"password", "token", "api", "key"})
and show
):
field.password = True
class MultilineFieldFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
key = field.name
if key in {
"suffix",
"prefix",
"template",
"examples",
"code",
"headers",
"description",
}:
field.multiline = True
class DefaultValueFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
value = field.to_dict()
if "default" in value:
field.value = value["default"]
class HeadersDefaultValueFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
key = field.name
if key == "headers":
field.value = """{'Authorization': 'Bearer <token>'}"""
class DictCodeFileFormatter(FieldFormatter):
def format(self, field: TemplateField, name: Optional[str] = None) -> None:
key = field.name
value = field.to_dict()
_type = value["type"]
if "dict" in _type.lower():
if key == "dict_":
field.field_type = "file"
field.suffixes = [".json", ".yaml", ".yml"]
field.file_types = ["json", "yaml", "yml"]
else:
field.field_type = "code"

View file

@ -1,10 +1,56 @@
import json
from typing import Optional from typing import Optional
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.constants import CTRANSFORMERS_DEFAULT_CONFIG
from langflow.template.frontend_node.constants import OPENAI_API_BASE_INFO
class LLMFrontendNode(FrontendNode): class LLMFrontendNode(FrontendNode):
def add_extra_fields(self) -> None:
if "VertexAI" in self.template.type_name:
# Add credentials field which should of type file.
self.template.add_field(
TemplateField(
field_type="file",
required=False,
show=True,
name="credentials",
value="",
suffixes=[".json"],
fileTypes=["json"],
)
)
@staticmethod
def format_vertex_field(field: TemplateField, name: str):
if "VertexAI" in name:
advanced_fields = [
"tuned_model_name",
"verbose",
"top_p",
"top_k",
"max_output_tokens",
]
if field.name in advanced_fields:
field.advanced = True
show_fields = [
"tuned_model_name",
"verbose",
"project",
"location",
"credentials",
"max_output_tokens",
"model_name",
"temperature",
"top_p",
"top_k",
]
if field.name in show_fields:
field.show = True
@staticmethod @staticmethod
def format_openai_field(field: TemplateField): def format_openai_field(field: TemplateField):
if "openai" in field.name.lower(): if "openai" in field.name.lower():
@ -15,6 +61,13 @@ class LLMFrontendNode(FrontendNode):
if "key" not in field.name.lower() and "token" not in field.name.lower(): if "key" not in field.name.lower() and "token" not in field.name.lower():
field.password = False field.password = False
if field.name == "openai_api_base":
field.info = OPENAI_API_BASE_INFO
def add_extra_base_classes(self) -> None:
if "BaseLLM" not in self.base_classes:
self.base_classes.append("BaseLLM")
@staticmethod @staticmethod
def format_azure_field(field: TemplateField): def format_azure_field(field: TemplateField):
if field.name == "model_name": if field.name == "model_name":
@ -31,6 +84,13 @@ class LLMFrontendNode(FrontendNode):
field.show = True field.show = True
field.advanced = not field.required field.advanced = not field.required
@staticmethod
def format_ctransformers_field(field: TemplateField):
if field.name == "config":
field.show = True
field.advanced = True
field.value = json.dumps(CTRANSFORMERS_DEFAULT_CONFIG, indent=2)
@staticmethod @staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:
display_names_dict = { display_names_dict = {
@ -38,10 +98,13 @@ class LLMFrontendNode(FrontendNode):
} }
FrontendNode.format_field(field, name) FrontendNode.format_field(field, name)
LLMFrontendNode.format_openai_field(field) LLMFrontendNode.format_openai_field(field)
LLMFrontendNode.format_ctransformers_field(field)
if name and "azure" in name.lower(): if name and "azure" in name.lower():
LLMFrontendNode.format_azure_field(field) LLMFrontendNode.format_azure_field(field)
if name and "llama" in name.lower(): if name and "llama" in name.lower():
LLMFrontendNode.format_llama_field(field) LLMFrontendNode.format_llama_field(field)
if name and "vertex" in name.lower():
LLMFrontendNode.format_vertex_field(field, name)
SHOW_FIELDS = ["repo_id"] SHOW_FIELDS = ["repo_id"]
if field.name in SHOW_FIELDS: if field.name in SHOW_FIELDS:
field.show = True field.show = True
@ -77,6 +140,17 @@ class LLMFrontendNode(FrontendNode):
"model_file", "model_file",
"model_type", "model_type",
"deployment_name", "deployment_name",
"credentials",
]: ]:
field.advanced = False field.advanced = False
field.show = True field.show = True
if field.name == "credentials":
field.field_type = "file"
if name == "VertexAI" and field.name not in [
"callbacks",
"client",
"stop",
"tags",
"cache",
]:
field.show = True

View file

@ -2,11 +2,19 @@ from typing import Optional
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.template.base import Template
from langchain.memory.chat_message_histories.postgres import DEFAULT_CONNECTION_STRING
class MemoryFrontendNode(FrontendNode): class MemoryFrontendNode(FrontendNode):
#! Needs testing #! Needs testing
def add_extra_fields(self) -> None: def add_extra_fields(self) -> None:
# chat history should have another way to add common field?
# prevent adding incorect field in ChatMessageHistory
base_message_classes = ["BaseEntityStore", "BaseChatMessageHistory"]
if any(base_class in self.base_classes for base_class in base_message_classes):
return
# add return_messages field # add return_messages field
self.template.add_field( self.template.add_field(
TemplateField( TemplateField(
@ -29,16 +37,17 @@ class MemoryFrontendNode(FrontendNode):
value="", value="",
) )
) )
self.template.add_field( if self.template.type_name not in {"VectorStoreRetrieverMemory"}:
TemplateField( self.template.add_field(
field_type="str", TemplateField(
required=False, field_type="str",
show=True, required=False,
name="output_key", show=True,
advanced=True, name="output_key",
value="", advanced=True,
value="",
)
) )
)
@staticmethod @staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:
@ -64,3 +73,50 @@ class MemoryFrontendNode(FrontendNode):
field.value = "" field.value = ""
if field.name == "memory_key": if field.name == "memory_key":
field.value = "chat_history" field.value = "chat_history"
if field.name == "chat_memory":
field.show = True
field.advanced = False
field.required = False
if field.name == "url":
field.show = True
if field.name == "entity_store":
field.show = True
if name == "SQLiteEntityStore":
field.show = True
class PostgresChatMessageHistoryFrontendNode(MemoryFrontendNode):
name: str = "PostgresChatMessageHistory"
template: Template = Template(
type_name="PostgresChatMessageHistory",
fields=[
TemplateField(
field_type="str",
required=True,
placeholder="",
is_list=False,
show=True,
multiline=False,
name="session_id",
),
TemplateField(
field_type="str",
required=True,
show=True,
name="connection_string",
value=DEFAULT_CONNECTION_STRING,
),
TemplateField(
field_type="str",
required=True,
placeholder="",
is_list=False,
show=True,
multiline=False,
value="message_store",
name="table_name",
),
],
)
description: str = "Memory store with Postgres"
base_classes: list[str] = ["PostgresChatMessageHistory", "BaseChatMessageHistory"]

View file

@ -0,0 +1,15 @@
from typing import Optional
from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode
class RetrieverFrontendNode(FrontendNode):
@staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
FrontendNode.format_field(field, name)
# Define common field attributes
field.show = True
if field.name == "parser_key":
field.display_name = "Parser Key"
field.password = False

View file

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

View file

@ -51,7 +51,7 @@ class VectorStoreFrontendNode(FrontendNode):
required=False, required=False,
show=True, show=True,
advanced=False, advanced=False,
value=True, value=False,
display_name="Persist", display_name="Persist",
) )
extra_fields.append(extra_field) extra_fields.append(extra_field)
@ -200,7 +200,7 @@ class VectorStoreFrontendNode(FrontendNode):
self.template.add_field(field) self.template.add_field(field)
def add_extra_base_classes(self) -> None: def add_extra_base_classes(self) -> None:
self.base_classes.append("BaseRetriever") self.base_classes.extend(("BaseRetriever", "VectorStoreRetriever"))
@staticmethod @staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:

View file

@ -165,6 +165,7 @@ def build_template_from_method(
"required": param.default == param.empty, "required": param.default == param.empty,
} }
for name, param in params.items() for name, param in params.items()
if name not in ["self", "kwargs", "args"]
}, },
} }
@ -233,6 +234,9 @@ def format_dict(d, name: Optional[str] = None):
_type = value["type"] _type = value["type"]
if not isinstance(_type, str):
_type = _type.__name__
# Remove 'Optional' wrapper # Remove 'Optional' wrapper
if "Optional" in _type: if "Optional" in _type:
_type = _type.replace("Optional[", "")[:-1] _type = _type.replace("Optional[", "")[:-1]

View file

@ -13,9 +13,11 @@
"@headlessui/react": "^1.7.10", "@headlessui/react": "^1.7.10",
"@heroicons/react": "^2.0.15", "@heroicons/react": "^2.0.15",
"@mui/material": "^5.11.9", "@mui/material": "^5.11.9",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.3", "@radix-ui/react-menubar": "^1.0.3",
"@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-progress": "^1.0.3",
@ -27,6 +29,7 @@
"@tabler/icons-react": "^2.18.0", "@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.4",
"accordion": "^3.0.2",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6", "add": "^2.0.6",
"ansi-to-html": "^0.7.2", "ansi-to-html": "^0.7.2",
@ -34,6 +37,7 @@
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0", "class-variance-authority": "^0.6.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"dompurify": "^3.0.3",
"esbuild": "^0.17.18", "esbuild": "^0.17.18",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lucide-react": "^0.233.0", "lucide-react": "^0.233.0",
@ -53,7 +57,7 @@
"rehype-mathjax": "^4.0.2", "rehype-mathjax": "^4.0.2",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"shadcn-ui": "^0.1.3", "shadcn-ui": "^0.2.2",
"short-unique-id": "^4.4.4", "short-unique-id": "^4.4.4",
"switch": "^0.0.0", "switch": "^0.0.0",
"table": "^6.8.1", "table": "^6.8.1",
@ -80,6 +84,8 @@
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"daisyui": "^3.1.1", "daisyui": "^3.1.1",
"postcss": "^8.4.23", "postcss": "^8.4.23",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "^3.3.2", "tailwindcss": "^3.3.2",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.3.9" "vite": "^4.3.9"
@ -114,6 +120,20 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@antfu/ni": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-0.21.4.tgz",
"integrity": "sha512-O0Uv9LbLDSoEg26fnMDdDRiPwFJnQSoD4WnrflDwKCJm8Cx/0mV4cGxwBLXan5mGIrpK4Dd7vizf4rQm0QCEAA==",
"bin": {
"na": "bin/na.mjs",
"nci": "bin/nci.mjs",
"ni": "bin/ni.mjs",
"nlx": "bin/nlx.mjs",
"nr": "bin/nr.mjs",
"nu": "bin/nu.mjs",
"nun": "bin/nun.mjs"
}
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.21.4", "version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
@ -1212,15 +1232,15 @@
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
}, },
"node_modules/@mui/system": { "node_modules/@mui/system": {
"version": "5.13.5", "version": "5.13.6",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.5.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.6.tgz",
"integrity": "sha512-n0gzUxoZ2ZHZgnExkh2Htvo9uW2oakofgPRQrDoa/GQOWyRD0NH9MDszBwOb6AAoXZb+OV5TE7I4LeZ/dzgHYA==", "integrity": "sha512-G3Xr28uLqU3DyF6r2LQkHGw/ku4P0AHzlKVe7FGXOPl7X1u+hoe2xxj8Vdiq/69II/mh9OP21i38yBWgWb7WgQ==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.21.0", "@babel/runtime": "^7.22.5",
"@mui/private-theming": "^5.13.1", "@mui/private-theming": "^5.13.1",
"@mui/styled-engine": "^5.13.2", "@mui/styled-engine": "^5.13.2",
"@mui/types": "^7.2.4", "@mui/types": "^7.2.4",
"@mui/utils": "^5.13.1", "@mui/utils": "^5.13.6",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"csstype": "^3.1.2", "csstype": "^3.1.2",
"prop-types": "^15.8.1" "prop-types": "^15.8.1"
@ -1280,11 +1300,11 @@
} }
}, },
"node_modules/@mui/utils": { "node_modules/@mui/utils": {
"version": "5.13.1", "version": "5.13.6",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.6.tgz",
"integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", "integrity": "sha512-ggNlxl5NPSbp+kNcQLmSig6WVB0Id+4gOxhx644987v4fsji+CSXc+MFYLocFB/x4oHtzCUlSzbVHlJfP/fXoQ==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.21.0", "@babel/runtime": "^7.22.5",
"@types/prop-types": "^15.7.5", "@types/prop-types": "^15.7.5",
"@types/react-is": "^18.2.0", "@types/react-is": "^18.2.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
@ -1366,6 +1386,37 @@
"@babel/runtime": "^7.13.10" "@babel/runtime": "^7.13.10"
} }
}, },
"node_modules/@radix-ui/react-accordion": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz",
"integrity": "sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-collapsible": "1.0.3",
"@radix-ui/react-collection": "1.0.3",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-direction": "1.0.1",
"@radix-ui/react-id": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-controllable-state": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-arrow": { "node_modules/@radix-ui/react-arrow": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
@ -1419,6 +1470,36 @@
} }
} }
}, },
"node_modules/@radix-ui/react-collapsible": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz",
"integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-id": "1.0.1",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-controllable-state": "1.0.1",
"@radix-ui/react-use-layout-effect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": { "node_modules/@radix-ui/react-collection": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
@ -1630,6 +1711,14 @@
} }
} }
}, },
"node_modules/@radix-ui/react-icons": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
"integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==",
"peerDependencies": {
"react": "^16.x || ^17.x || ^18.x"
}
},
"node_modules/@radix-ui/react-id": { "node_modules/@radix-ui/react-id": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
@ -3106,6 +3195,39 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@ts-morph/common": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.19.0.tgz",
"integrity": "sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==",
"dependencies": {
"fast-glob": "^3.2.12",
"minimatch": "^7.4.3",
"mkdirp": "^2.1.6",
"path-browserify": "^1.0.1"
}
},
"node_modules/@ts-morph/common/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@ts-morph/common/node_modules/minimatch": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz",
"integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@types/aria-query": { "node_modules/@types/aria-query": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz",
@ -3547,6 +3669,11 @@
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
}, },
"node_modules/accordion": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/accordion/-/accordion-3.0.2.tgz",
"integrity": "sha512-jbQfFaw+57OBwPt7qSNHuW+RA8smmRwkWRS1Ozh6K/QxUspBgBV/LpdSzlY7vee8TomS6j3D33B9rIeH1qMwsA=="
},
"node_modules/ace-builds": { "node_modules/ace-builds": {
"version": "1.16.0", "version": "1.16.0",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.16.0.tgz", "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.16.0.tgz",
@ -3714,9 +3841,9 @@
} }
}, },
"node_modules/aria-query": { "node_modules/aria-query": {
"version": "5.2.1", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.2.1.tgz", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"dequal": "^2.0.3" "dequal": "^2.0.3"
@ -4491,6 +4618,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/code-block-writer": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w=="
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -5009,10 +5141,15 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/dompurify": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.3.tgz",
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ=="
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.438", "version": "1.4.440",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.438.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz",
"integrity": "sha512-x94U0FhphEsHsOloCvlsujHCvoir0ZQ73ZAs/QN4PLx98uNvyEU79F75rq1db75Bx/atvuh7KPeuxelh+xfYJw==" "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw=="
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
@ -6745,9 +6882,9 @@
} }
}, },
"node_modules/katex": { "node_modules/katex": {
"version": "0.16.7", "version": "0.16.8",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.7.tgz", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
"integrity": "sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==", "integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
"funding": [ "funding": [
"https://opencollective.com/katex", "https://opencollective.com/katex",
"https://github.com/sponsors/katex" "https://github.com/sponsors/katex"
@ -6863,9 +7000,9 @@
} }
}, },
"node_modules/log-symbols/node_modules/chalk": { "node_modules/log-symbols/node_modules/chalk": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
"integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"engines": { "engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0" "node": "^12.17.0 || ^14.13 || >=16.0.0"
}, },
@ -7838,11 +7975,33 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mj-context-menu": { "node_modules/mj-context-menu": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz",
"integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==" "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA=="
}, },
"node_modules/mkdirp": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mri": { "node_modules/mri": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@ -8111,9 +8270,9 @@
} }
}, },
"node_modules/ora/node_modules/chalk": { "node_modules/ora/node_modules/chalk": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
"integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"engines": { "engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0" "node": "^12.17.0 || ^14.13 || >=16.0.0"
}, },
@ -8210,6 +8369,11 @@
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
}, },
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -8437,6 +8601,95 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-tailwindcss": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.3.0.tgz",
"integrity": "sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==",
"dev": true,
"engines": {
"node": ">=12.17.0"
},
"peerDependencies": {
"@ianvs/prettier-plugin-sort-imports": "*",
"@prettier/plugin-pug": "*",
"@shopify/prettier-plugin-liquid": "*",
"@shufo/prettier-plugin-blade": "*",
"@trivago/prettier-plugin-sort-imports": "*",
"prettier": ">=2.2.0",
"prettier-plugin-astro": "*",
"prettier-plugin-css-order": "*",
"prettier-plugin-import-sort": "*",
"prettier-plugin-jsdoc": "*",
"prettier-plugin-marko": "*",
"prettier-plugin-organize-attributes": "*",
"prettier-plugin-organize-imports": "*",
"prettier-plugin-style-order": "*",
"prettier-plugin-svelte": "*",
"prettier-plugin-twig-melody": "*"
},
"peerDependenciesMeta": {
"@ianvs/prettier-plugin-sort-imports": {
"optional": true
},
"@prettier/plugin-pug": {
"optional": true
},
"@shopify/prettier-plugin-liquid": {
"optional": true
},
"@shufo/prettier-plugin-blade": {
"optional": true
},
"@trivago/prettier-plugin-sort-imports": {
"optional": true
},
"prettier-plugin-astro": {
"optional": true
},
"prettier-plugin-css-order": {
"optional": true
},
"prettier-plugin-import-sort": {
"optional": true
},
"prettier-plugin-jsdoc": {
"optional": true
},
"prettier-plugin-marko": {
"optional": true
},
"prettier-plugin-organize-attributes": {
"optional": true
},
"prettier-plugin-organize-imports": {
"optional": true
},
"prettier-plugin-style-order": {
"optional": true
},
"prettier-plugin-svelte": {
"optional": true
},
"prettier-plugin-twig-melody": {
"optional": true
}
}
},
"node_modules/pretty-format": { "node_modules/pretty-format": {
"version": "27.5.1", "version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
@ -9286,23 +9539,40 @@
} }
}, },
"node_modules/shadcn-ui": { "node_modules/shadcn-ui": {
"version": "0.1.3", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/shadcn-ui/-/shadcn-ui-0.1.3.tgz", "resolved": "https://registry.npmjs.org/shadcn-ui/-/shadcn-ui-0.2.2.tgz",
"integrity": "sha512-f6Wa4ZIxsigfOonC3yyJkPb2JXJnuGFyUn1fJJrDUHvIJOydUukcdQsZg7Lp6F6llkmfRjra1dZOo0KpSfdjuQ==", "integrity": "sha512-T76EeZymSB45Yz63gkYOv9P0Ke+UA9IZenysx+975nyNzXxU7HRBgfwuHiMcrcubtOLrzRVedTLX3lcOMqDeRQ==",
"dependencies": { "dependencies": {
"@antfu/ni": "^0.21.4",
"chalk": "5.2.0", "chalk": "5.2.0",
"commander": "^10.0.0", "commander": "^10.0.0",
"cosmiconfig": "^8.1.3",
"diff": "^5.1.0",
"execa": "^7.0.0", "execa": "^7.0.0",
"fs-extra": "^11.1.0", "fs-extra": "^11.1.0",
"https-proxy-agent": "^6.2.0",
"node-fetch": "^3.3.0", "node-fetch": "^3.3.0",
"ora": "^6.1.2", "ora": "^6.1.2",
"prompts": "^2.4.2", "prompts": "^2.4.2",
"ts-morph": "^18.0.0",
"tsconfig-paths": "^4.2.0",
"zod": "^3.20.2" "zod": "^3.20.2"
}, },
"bin": { "bin": {
"shadcn-ui": "dist/index.js" "shadcn-ui": "dist/index.js"
} }
}, },
"node_modules/shadcn-ui/node_modules/agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/shadcn-ui/node_modules/chalk": { "node_modules/shadcn-ui/node_modules/chalk": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
@ -9322,6 +9592,35 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/shadcn-ui/node_modules/cosmiconfig": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
"integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==",
"dependencies": {
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/shadcn-ui/node_modules/https-proxy-agent": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz",
"integrity": "sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA==",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -9575,6 +9874,14 @@
"url": "https://github.com/chalk/ansi-regex?sponsor=1" "url": "https://github.com/chalk/ansi-regex?sponsor=1"
} }
}, },
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"engines": {
"node": ">=4"
}
},
"node_modules/strip-eof": { "node_modules/strip-eof": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
@ -9950,6 +10257,28 @@
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
}, },
"node_modules/ts-morph": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-18.0.0.tgz",
"integrity": "sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==",
"dependencies": {
"@ts-morph/common": "~0.19.0",
"code-block-writer": "^12.0.0"
}
},
"node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.5.3", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",

View file

@ -8,9 +8,11 @@
"@headlessui/react": "^1.7.10", "@headlessui/react": "^1.7.10",
"@heroicons/react": "^2.0.15", "@heroicons/react": "^2.0.15",
"@mui/material": "^5.11.9", "@mui/material": "^5.11.9",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.3", "@radix-ui/react-menubar": "^1.0.3",
"@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-progress": "^1.0.3",
@ -22,6 +24,7 @@
"@tabler/icons-react": "^2.18.0", "@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/line-clamp": "^0.4.4",
"accordion": "^3.0.2",
"ace-builds": "^1.16.0", "ace-builds": "^1.16.0",
"add": "^2.0.6", "add": "^2.0.6",
"ansi-to-html": "^0.7.2", "ansi-to-html": "^0.7.2",
@ -29,6 +32,7 @@
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0", "class-variance-authority": "^0.6.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"dompurify": "^3.0.3",
"esbuild": "^0.17.18", "esbuild": "^0.17.18",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lucide-react": "^0.233.0", "lucide-react": "^0.233.0",
@ -48,7 +52,7 @@
"rehype-mathjax": "^4.0.2", "rehype-mathjax": "^4.0.2",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"shadcn-ui": "^0.1.3", "shadcn-ui": "^0.2.2",
"short-unique-id": "^4.4.4", "short-unique-id": "^4.4.4",
"switch": "^0.0.0", "switch": "^0.0.0",
"table": "^6.8.1", "table": "^6.8.1",
@ -101,6 +105,8 @@
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"daisyui": "^3.1.1", "daisyui": "^3.1.1",
"postcss": "^8.4.23", "postcss": "^8.4.23",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "^3.3.2", "tailwindcss": "^3.3.2",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.3.9" "vite": "^4.3.9"

View file

@ -0,0 +1,3 @@
module.exports = {
plugins: [require("prettier-plugin-tailwindcss")],
};

View file

@ -51,6 +51,13 @@ export default function App() {
useEffect(() => { useEffect(() => {
// If there is an error alert open with data, add it to the alertsList // If there is an error alert open with data, add it to the alertsList
if (errorOpen && errorData) { if (errorOpen && errorData) {
if (
alertsList.length > 0 &&
JSON.stringify(alertsList[alertsList.length - 1].data) ===
JSON.stringify(errorData)
) {
return;
}
setErrorOpen(false); setErrorOpen(false);
setAlertsList((old) => { setAlertsList((old) => {
let newAlertsList = [ let newAlertsList = [
@ -62,6 +69,13 @@ export default function App() {
} }
// If there is a notice alert open with data, add it to the alertsList // If there is a notice alert open with data, add it to the alertsList
else if (noticeOpen && noticeData) { else if (noticeOpen && noticeData) {
if (
alertsList.length > 0 &&
JSON.stringify(alertsList[alertsList.length - 1].data) ===
JSON.stringify(noticeData)
) {
return;
}
setNoticeOpen(false); setNoticeOpen(false);
setAlertsList((old) => { setAlertsList((old) => {
let newAlertsList = [ let newAlertsList = [
@ -73,6 +87,13 @@ export default function App() {
} }
// If there is a success alert open with data, add it to the alertsList // If there is a success alert open with data, add it to the alertsList
else if (successOpen && successData) { else if (successOpen && successData) {
if (
alertsList.length > 0 &&
JSON.stringify(alertsList[alertsList.length - 1].data) ===
JSON.stringify(successData)
) {
return;
}
setSuccessOpen(false); setSuccessOpen(false);
setAlertsList((old) => { setAlertsList((old) => {
let newAlertsList = [ let newAlertsList = [

View file

@ -25,6 +25,7 @@ import { nodeColors } from "../../../../utils";
import ShadTooltip from "../../../../components/ShadTooltipComponent"; import ShadTooltip from "../../../../components/ShadTooltipComponent";
import { PopUpContext } from "../../../../contexts/popUpContext"; import { PopUpContext } from "../../../../contexts/popUpContext";
import ToggleShadComponent from "../../../../components/toggleShadComponent"; import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Info } from "lucide-react";
export default function ParameterComponent({ export default function ParameterComponent({
left, left,
@ -36,9 +37,11 @@ export default function ParameterComponent({
type, type,
name = "", name = "",
required = false, required = false,
info = "",
}: ParameterComponentType) { }: ParameterComponentType) {
const ref = useRef(null); const ref = useRef(null);
const refHtml = useRef(null); const refHtml = useRef(null);
const infoHtml = useRef(null);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0); const [position, setPosition] = useState(0);
const { closePopUp } = useContext(PopUpContext); const { closePopUp } = useContext(PopUpContext);
@ -79,6 +82,18 @@ 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(() => { useEffect(() => {
const groupedObj = groupByFamily(myData, tooltipTitle); const groupedObj = groupByFamily(myData, tooltipTitle);
@ -126,9 +141,22 @@ export default function ParameterComponent({
className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2"
> >
<> <>
<div className={"w-full truncate text-sm " + (left ? "" : "text-end")}> <div
className={
"w-full truncate text-sm" +
(left ? "" : " text-end") +
(info !== "" ? " flex items-center" : "")
}
>
{title} {title}
<span className="text-destructive">{required ? " *" : ""}</span> <span className="text-destructive">{required ? " *" : ""}</span>
<div className="">
{info !== "" && (
<ShadTooltip content={infoHtml.current}>
<Info className="relative bottom-0.5 ml-2 h-3 w-3" />
</ShadTooltip>
)}
</div>
</div> </div>
{left && {left &&
(type === "str" || (type === "str" ||
@ -144,7 +172,6 @@ export default function ParameterComponent({
delayDuration={0} delayDuration={0}
content={refHtml.current} content={refHtml.current}
side={left ? "left" : "right"} side={left ? "left" : "right"}
open={refHtml?.current?.length > 0}
> >
<Handle <Handle
type={left ? "target" : "source"} type={left ? "target" : "source"}

View file

@ -6,16 +6,7 @@ import {
} from "../../utils"; } from "../../utils";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { import { useContext, useState, useEffect, useRef } from "react";
useContext,
useState,
useEffect,
useRef,
ForwardRefExoticComponent,
ComponentType,
SVGProps,
ReactNode,
} from "react";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
@ -23,10 +14,8 @@ import NodeModal from "../../modals/NodeModal";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
import { NodeToolbar } from "reactflow"; import { NodeToolbar } from "reactflow";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import { useSSE } from "../../contexts/SSEContext"; import { useSSE } from "../../contexts/SSEContext";
import { ReactElement } from "react-markdown/lib/react-markdown";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -46,6 +35,7 @@ export default function GenericNode({
const [validationStatus, setValidationStatus] = useState(null); const [validationStatus, setValidationStatus] = useState(null);
// State for outline color // State for outline color
const { sseData, isBuilding } = useSSE(); const { sseData, isBuilding } = useSSE();
const refHtml = useRef(null);
// useEffect(() => { // useEffect(() => {
// if (reactFlowInstance) { // if (reactFlowInstance) {
@ -144,7 +134,7 @@ export default function GenericNode({
<div <div
className={classNames( className={classNames(
validationStatus && validationStatus.valid validationStatus && validationStatus.valid
? "h-4 w-4 rounded-full bg-status-red opacity-100" ? "h-4 w-4 rounded-full bg-status-green opacity-100"
: "hidden h-4 w-4 animate-spin rounded-full bg-ring opacity-0", : "hidden h-4 w-4 animate-spin rounded-full bg-ring opacity-0",
"hover: absolute w-4 transition-all duration-200 ease-in-out hover:text-ring" "hover: absolute w-4 transition-all duration-200 ease-in-out hover:text-ring"
)} )}
@ -215,6 +205,7 @@ export default function GenericNode({
? toTitleCase(data.node.template[t].name) ? toTitleCase(data.node.template[t].name)
: toTitleCase(t) : toTitleCase(t)
} }
info={data.node.template[t].info}
name={t} name={t}
tooltipTitle={data.node.template[t].type} tooltipTitle={data.node.template[t].type}
required={data.node.template[t].required} required={data.node.template[t].required}

View file

@ -0,0 +1,56 @@
import { ReactElement, useContext, useEffect, useRef, useState } from "react";
import {
AccordionComponentType,
ProgressBarType,
} from "../../types/components";
import { Progress } from "../../components/ui/progress";
import { setInterval } from "timers/promises";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../components/ui/accordion";
export default function AccordionComponent({
trigger,
children,
open = [],
}: AccordionComponentType) {
const [value, setValue] = useState(
open.length == 0 ? "" : getOpenAccordion()
);
function getOpenAccordion() {
let value = "";
open.forEach((el) => {
if (el == trigger) {
value = trigger;
}
});
return value;
}
function handleClick() {
value == "" ? setValue(trigger) : setValue("");
}
return (
<>
<Accordion type="single" value={value} onValueChange={setValue}>
<AccordionItem value={trigger} className="border-none">
<AccordionTrigger
onClick={() => {
handleClick();
}}
className="ml-3"
>
{trigger}
</AccordionTrigger>
<AccordionContent>{children}</AccordionContent>
</AccordionItem>
</Accordion>
</>
);
}

View file

@ -1,3 +1,4 @@
import { ShadTooltipProps } from "../../types/components";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
@ -5,18 +6,19 @@ import {
TooltipTrigger, TooltipTrigger,
} from "../ui/tooltip"; } from "../ui/tooltip";
const ShadTooltip = (props) => { const ShadTooltip = ({
delayDuration = 500,
side,
content,
children,
}: ShadTooltipProps) => {
return ( return (
<TooltipProvider> <TooltipProvider>
<Tooltip delayDuration={props.delayDuration}> <Tooltip delayDuration={delayDuration}>
<TooltipTrigger asChild>{props.children}</TooltipTrigger> <TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent <TooltipContent side={side} avoidCollisions={false} sticky="always">
side={props.side} {content}
avoidCollisions={false}
sticky="always"
>
{props.content}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>

View file

@ -2,9 +2,9 @@ import { useState } from "react";
import { ChatMessageType } from "../../../types/chat"; import { ChatMessageType } from "../../../types/chat";
import { nodeColors } from "../../../utils"; import { nodeColors } from "../../../utils";
import Convert from "ansi-to-html"; import Convert from "ansi-to-html";
const convert = new Convert({ newline: true });
import { MessageCircle } from "lucide-react"; import { MessageCircle } from "lucide-react";
import DOMPurify from "dompurify";
const convert = new Convert({ newline: true });
export default function ChatMessage({ chat }: { chat: ChatMessageType }) { export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
const [hidden, setHidden] = useState(true); const [hidden, setHidden] = useState(true);
return ( return (
@ -23,13 +23,14 @@ export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
<MessageCircle className="h-5 w-5 animate-bounce" /> <MessageCircle className="h-5 w-5 animate-bounce" />
</div> </div>
)} )}
{chat.thought && chat.thought !== "" && !hidden && ( {chat.thought && chat.thought !== "" && !hidden && (
<div <div
onClick={() => setHidden((prev) => !prev)} onClick={() => setHidden((prev) => !prev)}
style={{ backgroundColor: nodeColors["thought"] }} style={{ backgroundColor: nodeColors["thought"] }}
className=" inline-block w-full cursor-pointer px-5 pb-3 pt-3 text-start" className=" inline-block w-full cursor-pointer px-5 pb-3 pt-3 text-start"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: convert.toHtml(chat.thought), __html: DOMPurify.sanitize(convert.toHtml(chat.thought)),
}} }}
></div> ></div>
)} )}

View file

@ -17,7 +17,9 @@ export default function CodeAreaComponent({
disabled, disabled,
editNode = false, editNode = false,
}: TextAreaComponentType) { }: TextAreaComponentType) {
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(
typeof value == "string" ? value : JSON.stringify(value)
);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
@ -27,7 +29,7 @@ export default function CodeAreaComponent({
}, [disabled, onChange]); }, [disabled, onChange]);
useEffect(() => { useEffect(() => {
setMyValue(value); setMyValue(typeof value == "string" ? value : JSON.stringify(value));
}, [value]); }, [value]);
return ( return (

View file

@ -1,9 +1,11 @@
import { Listbox, Transition } from "@headlessui/react"; import { Listbox, Transition } from "@headlessui/react";
import { Fragment, useEffect, useState } from "react"; import { Fragment, useContext, useEffect, useState } from "react";
import { DropDownComponentType } from "../../types/components"; import { DropDownComponentType } from "../../types/components";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants"; import { INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants";
import { ChevronsUpDown, Check } from "lucide-react"; import { ChevronsUpDown, Check } from "lucide-react";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
export default function Dropdown({ export default function Dropdown({
value, value,
@ -11,13 +13,17 @@ export default function Dropdown({
onSelect, onSelect,
editNode = false, editNode = false,
numberOfOptions = 0, numberOfOptions = 0,
apiModal = false,
}: DropDownComponentType) { }: DropDownComponentType) {
const { closePopUp } = useContext(PopUpContext);
let [internalValue, setInternalValue] = useState( let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value value === "" || !value ? "Choose an option" : value
); );
useEffect(() => { useEffect(() => {
setInternalValue(value === "" || !value ? "Choose an option" : value); setInternalValue(value === "" || !value ? "Choose an option" : value);
}, [value]); }, [closePopUp]);
return ( return (
<> <>
@ -61,11 +67,12 @@ export default function Dropdown({
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<Listbox.Options <Listbox.Options
className={ className={classNames(
editNode editNode
? "absolute z-10 mt-1 max-h-60 w-[215px] overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" ? "z-10 mt-1 max-h-60 w-[215px] overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
: "nowheel overflow-y absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm " : "nowheel overflow-y z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-background py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm ",
} apiModal ? "mb-2 w-[250px]" : "absolute"
)}
> >
{options.map((option, id) => ( {options.map((option, id) => (
<Listbox.Option <Listbox.Option

View file

@ -2,6 +2,7 @@ import { useContext, useEffect, useState } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { INPUT_DISABLE, INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants"; import { INPUT_DISABLE, INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants";
import { PopUpContext } from "../../contexts/popUpContext";
export default function FloatComponent({ export default function FloatComponent({
value, value,
@ -12,6 +13,7 @@ export default function FloatComponent({
}: FloatComponentType) { }: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
const { closePopUp } = useContext(PopUpContext);
const step = 0.1; const step = 0.1;
const min = 0; const min = 0;
@ -26,7 +28,7 @@ export default function FloatComponent({
useEffect(() => { useEffect(() => {
setMyValue(value); setMyValue(value);
}, [value]); }, [closePopUp]);
return ( return (
<div <div

View file

@ -98,7 +98,7 @@ export default function InputFileComponent({
disabled ? "pointer-events-none w-full cursor-not-allowed" : "w-full" disabled ? "pointer-events-none w-full cursor-not-allowed" : "w-full"
} }
> >
<div className="flex w-full items-center gap-2"> <div className="flex w-full items-center">
<span <span
onClick={handleButtonClick} onClick={handleButtonClick}
className={ className={
@ -111,7 +111,7 @@ export default function InputFileComponent({
</span> </span>
<button onClick={handleButtonClick}> <button onClick={handleButtonClick}>
{!editNode && !loading && ( {!editNode && !loading && (
<FileSearch2 className="h-6 w-6 hover:text-ring" /> <FileSearch2 className="ml-3 h-6 w-6 hover:text-ring" />
)} )}
{!editNode && loading && ( {!editNode && loading && (
<span className="loading loading-spinner loading-sm pointer-events-none h-8 pl-3"></span> <span className="loading loading-spinner loading-sm pointer-events-none h-8 pl-3"></span>

View file

@ -5,20 +5,29 @@ import { TabsContext } from "../../contexts/tabsContext";
import _ from "lodash"; import _ from "lodash";
import { INPUT_DISABLE, INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants"; import { INPUT_DISABLE, INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants";
import { X, Plus } from "lucide-react"; import { X, Plus } from "lucide-react";
import { PopUpContext } from "../../contexts/popUpContext";
export default function InputListComponent({ export default function InputListComponent({
value, value,
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
onAddInput,
}: InputListComponentType) { }: InputListComponentType) {
const [inputList, setInputList] = useState(value ?? [""]); const [inputList, setInputList] = useState(value ?? [""]);
const { closePopUp } = useContext(PopUpContext);
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
setInputList([""]); setInputList([""]);
onChange([""]); onChange([""]);
} }
}, [disabled, onChange]); }, [disabled, onChange]);
useEffect(() => {
setInputList(value);
}, [closePopUp]);
return ( return (
<div <div
className={ className={

View file

@ -3,6 +3,7 @@ import { FloatComponentType } from "../../types/components";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { INPUT_DISABLE, INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants"; import { INPUT_DISABLE, INPUT_EDIT_NODE, INPUT_STYLE } from "../../constants";
import { PopUpContext } from "../../contexts/popUpContext";
export default function IntComponent({ export default function IntComponent({
value, value,
@ -14,6 +15,7 @@ export default function IntComponent({
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
const min = 0; const min = 0;
const { closePopUp } = useContext(PopUpContext);
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
@ -24,7 +26,7 @@ export default function IntComponent({
useEffect(() => { useEffect(() => {
setMyValue(value); setMyValue(value);
}, [value]); }, [closePopUp]);
return ( return (
<div <div

View file

@ -36,7 +36,7 @@ export default function PromptAreaComponent({
disabled ? "pointer-events-none w-full cursor-not-allowed" : " w-full" disabled ? "pointer-events-none w-full cursor-not-allowed" : " w-full"
} }
> >
<div className="flex w-full items-center gap-3"> <div className="flex w-full items-center">
<span <span
onClick={() => { onClick={() => {
openPopUp( openPopUp(

View file

@ -0,0 +1,59 @@
"use client";
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { cn } from "../../utils";
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<ChevronDownIcon className="h-4 w-4 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className={cn(
"overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
className
)}
{...props}
>
<div className="pb-4 pt-0">{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View file

@ -54,7 +54,7 @@ export const TEXT_DIALOG_SUBTITLE = "Edit your text.";
* @param {string} flowId - The id of the flow * @param {string} flowId - The id of the flow
* @returns {string} - The python code * @returns {string} - The python code
*/ */
export const getPythonApiCode = (flow: FlowType): string => { export const getPythonApiCode = (flow: FlowType, tweak?): string => {
const flowId = flow.id; const flowId = flow.id;
// create a dictionary of node ids and the values is an empty dictionary // create a dictionary of node ids and the values is an empty dictionary
@ -70,7 +70,11 @@ BASE_API_URL = "${window.location.protocol}//${
FLOW_ID = "${flowId}" FLOW_ID = "${flowId}"
# You can tweak the flow by adding a tweaks dictionary # You can tweak the flow by adding a tweaks dictionary
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}} # e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
TWEAKS = ${JSON.stringify(tweaks, null, 2)} TWEAKS = ${
tweak && tweak.length > 0
? buildTweakObject(tweak)
: JSON.stringify(tweaks, null, 2)
}
def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict: def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
""" """
@ -100,7 +104,7 @@ print(run_flow("Your message", flow_id=FLOW_ID, tweaks=TWEAKS))`;
* @param {string} flowId - The id of the flow * @param {string} flowId - The id of the flow
* @returns {string} - The curl code * @returns {string} - The curl code
*/ */
export const getCurlCode = (flow: FlowType): string => { export const getCurlCode = (flow: FlowType, tweak?): string => {
const flowId = flow.id; const flowId = flow.id;
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
return `curl -X POST \\ return `curl -X POST \\
@ -108,27 +112,46 @@ export const getCurlCode = (flow: FlowType): string => {
window.location.host window.location.host
}/api/v1/process/${flowId} \\ }/api/v1/process/${flowId} \\
-H 'Content-Type: application/json' \\ -H 'Content-Type: application/json' \\
-d '{"inputs": {"input": message}, "tweaks": ${JSON.stringify( -d '{"inputs": {"input": message}, "tweaks": ${
tweaks, tweak && tweak.length > 0
null, ? buildTweakObject(tweak)
2 : JSON.stringify(tweaks, null, 2)
)}}'`; }}'`;
}; };
/** /**
* Function to get the python code for the API * Function to get the python code for the API
* @param {string} flowName - The name of the flow * @param {string} flowName - The name of the flow
* @returns {string} - The python code * @returns {string} - The python code
*/ */
export const getPythonCode = (flow: FlowType): string => { export const getPythonCode = (flow: FlowType, tweak?): string => {
const flowName = flow.name; const flowName = flow.name;
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
return `from langflow import load_flow_from_json return `from langflow import load_flow_from_json
TWEAKS = ${JSON.stringify(tweaks, null, 2)} TWEAKS = ${
tweak && tweak.length > 0
? buildTweakObject(tweak)
: JSON.stringify(tweaks, null, 2)
}
flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS) flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS)
# Now you can use it like any chain # Now you can use it like any chain
flow("Hey, have you heard of LangFlow?")`; flow("Hey, have you heard of LangFlow?")`;
}; };
function buildTweakObject(tweak) {
tweak.forEach((el) => {
Object.keys(el).forEach((key) => {
for (let kp in el[key]) {
try {
el[key][kp] = JSON.parse(el[key][kp]);
} catch {}
}
});
});
const tweakString = JSON.stringify(tweak, null, 2);
return tweakString;
}
/** /**
* The base text for subtitle of Import Dialog * The base text for subtitle of Import Dialog
* @constant * @constant
@ -195,257 +218,6 @@ export const NAV_DISPLAY_STYLE =
export const BUTTON_DIV_STYLE = " flex gap-2 "; export const BUTTON_DIV_STYLE = " flex gap-2 ";
(" focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none "); (" focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ");
/**
* Default description for the flow
* @constant
*/
export const DESCRIPTIONS: string[] = [
"Chain the Words, Master Language!",
"Language Architect at Work!",
"Empowering Language Engineering.",
"Craft Language Connections Here.",
"Create, Connect, Converse.",
"Smart Chains, Smarter Conversations.",
"Bridging Prompts for Brilliance.",
"Language Models, Unleashed.",
"Your Hub for Text Generation.",
"Promptly Ingenious!",
"Building Linguistic Labyrinths.",
"LangFlow: Create, Chain, Communicate.",
"Connect the Dots, Craft Language.",
"Interactive Language Weaving.",
"Generate, Innovate, Communicate.",
"Conversation Catalyst Engine.",
"Language Chainlink Master.",
"Design Dialogues with LangFlow.",
"Nurture NLP Nodes Here.",
"Conversational Cartography Unlocked.",
"Design, Develop, Dialogize.",
];
/**
* Adjectives for the name of the flow
* @constant
*
*/
export const ADJECTIVES: string[] = [
"admiring",
"adoring",
"agitated",
"amazing",
"angry",
"awesome",
"backstabbing",
"berserk",
"big",
"boring",
"clever",
"cocky",
"compassionate",
"condescending",
"cranky",
"desperate",
"determined",
"distracted",
"dreamy",
"drunk",
"ecstatic",
"elated",
"elegant",
"evil",
"fervent",
"focused",
"furious",
"gigantic",
"gloomy",
"goofy",
"grave",
"happy",
"high",
"hopeful",
"hungry",
"insane",
"jolly",
"jovial",
"kickass",
"lonely",
"loving",
"mad",
"modest",
"naughty",
"nauseous",
"nostalgic",
"pedantic",
"pensive",
"prickly",
"reverent",
"romantic",
"sad",
"serene",
"sharp",
"sick",
"silly",
"sleepy",
"small",
"stoic",
"stupefied",
"suspicious",
"tender",
"thirsty",
"tiny",
"trusting",
];
/**
* Nouns for the name of the flow
* @constant
*
*/
export const NOUNS: string[] = [
"albattani",
"allen",
"almeida",
"archimedes",
"ardinghelli",
"aryabhata",
"austin",
"babbage",
"banach",
"bardeen",
"bartik",
"bassi",
"bell",
"bhabha",
"bhaskara",
"blackwell",
"bohr",
"booth",
"borg",
"bose",
"boyd",
"brahmagupta",
"brattain",
"brown",
"carson",
"chandrasekhar",
"colden",
"cori",
"cray",
"curie",
"darwin",
"davinci",
"dijkstra",
"dubinsky",
"easley",
"einstein",
"elion",
"engelbart",
"euclid",
"euler",
"fermat",
"fermi",
"feynman",
"franklin",
"galileo",
"gates",
"goldberg",
"goldstine",
"goldwasser",
"golick",
"goodall",
"hamilton",
"hawking",
"heisenberg",
"heyrovsky",
"hodgkin",
"hoover",
"hopper",
"hugle",
"hypatia",
"jang",
"jennings",
"jepsen",
"joliot",
"jones",
"kalam",
"kare",
"keller",
"khorana",
"kilby",
"kirch",
"knuth",
"kowalevski",
"lalande",
"lamarr",
"leakey",
"leavitt",
"lichterman",
"liskov",
"lovelace",
"lumiere",
"mahavira",
"mayer",
"mccarthy",
"mcclintock",
"mclean",
"mcnulty",
"meitner",
"meninsky",
"mestorf",
"minsky",
"mirzakhani",
"morse",
"murdock",
"newton",
"nobel",
"noether",
"northcutt",
"noyce",
"panini",
"pare",
"pasteur",
"payne",
"perlman",
"pike",
"poincare",
"poitras",
"ptolemy",
"raman",
"ramanujan",
"ride",
"ritchie",
"roentgen",
"rosalind",
"saha",
"sammet",
"shaw",
"shirley",
"shockley",
"sinoussi",
"snyder",
"spence",
"stallman",
"stonebraker",
"swanson",
"swartz",
"swirles",
"tesla",
"thompson",
"torvalds",
"turing",
"varahamihira",
"visvesvaraya",
"volhard",
"wescoff",
"williams",
"wilson",
"wing",
"wozniak",
"wright",
"yalow",
"yonath",
];
/** /**
* Header text for user projects * Header text for user projects
* @constant * @constant

View file

@ -78,9 +78,9 @@ export function AlertProvider({ children }: { children: ReactNode }) {
* @param newState An object containing the new error data, including title and optional list of error messages * @param newState An object containing the new error data, including title and optional list of error messages
*/ */
function setErrorData(newState: { title: string; list?: Array<string> }) { function setErrorData(newState: { title: string; list?: Array<string> }) {
setErrorDataState(newState);
setErrorOpen(true);
if (newState.title && newState.title !== "") { if (newState.title && newState.title !== "") {
setErrorDataState(newState);
setErrorOpen(true);
setNotificationCenter(true); setNotificationCenter(true);
pushNotificationList({ pushNotificationList({
type: "error", type: "error",
@ -95,9 +95,9 @@ export function AlertProvider({ children }: { children: ReactNode }) {
* @param newState An object containing the title of the notice and optionally a link. * @param newState An object containing the title of the notice and optionally a link.
*/ */
function setNoticeData(newState: { title: string; link?: string }) { function setNoticeData(newState: { title: string; link?: string }) {
setNoticeDataState(newState);
setNoticeOpen(true);
if (newState.title && newState.title !== "") { if (newState.title && newState.title !== "") {
setNoticeDataState(newState);
setNoticeOpen(true);
// Add new notice to notification center // Add new notice to notification center
setNotificationCenter(true); setNotificationCenter(true);
pushNotificationList({ pushNotificationList({
@ -113,11 +113,10 @@ export function AlertProvider({ children }: { children: ReactNode }) {
* @param newState - A state object with a "title" property to set in the success data state. * @param newState - A state object with a "title" property to set in the success data state.
*/ */
function setSuccessData(newState: { title: string }) { function setSuccessData(newState: { title: string }) {
setSuccessDataState(newState); // update the success data state with the provided new state
setSuccessOpen(true); // open the success alert
// If the new state has a "title" property, add a new success notification to the list // If the new state has a "title" property, add a new success notification to the list
if (newState.title && newState.title !== "") { if (newState.title && newState.title !== "") {
setSuccessDataState(newState); // update the success data state with the provided new state
setSuccessOpen(true); // open the success alert
setNotificationCenter(true); // show the notification center setNotificationCenter(true); // show the notification center
pushNotificationList({ pushNotificationList({
// add the new notification to the list // add the new notification to the list

View file

@ -5,6 +5,8 @@ import React, { useState } from "react";
export const PopUpContext = createContext({ export const PopUpContext = createContext({
openPopUp: (popUpElement: JSX.Element) => {}, openPopUp: (popUpElement: JSX.Element) => {},
closePopUp: () => {}, closePopUp: () => {},
setCloseEdit: (value: string) => {},
closeEdit: "",
}); });
interface PopUpProviderProps { interface PopUpProviderProps {
@ -22,8 +24,12 @@ const PopUpProvider = ({ children }: PopUpProviderProps) => {
setPopUpElements((prevPopUps) => prevPopUps.slice(1)); setPopUpElements((prevPopUps) => prevPopUps.slice(1));
}; };
const [closeEdit, setCloseEdit] = useState("");
return ( return (
<PopUpContext.Provider value={{ openPopUp, closePopUp }}> <PopUpContext.Provider
value={{ openPopUp, closePopUp, closeEdit, setCloseEdit }}
>
{children} {children}
{popUpElements[0]} {popUpElements[0]}
</PopUpContext.Provider> </PopUpContext.Provider>

View file

@ -53,6 +53,8 @@ const TabsContextInitialValue: TabsContextType = {
tabsState: {}, tabsState: {},
setTabsState: (state: TabsState) => {}, setTabsState: (state: TabsState) => {},
getNodeId: (nodeType: string) => "", getNodeId: (nodeType: string) => "",
setTweak: (tweak: any) => {},
getTweak: {},
paste: ( paste: (
selection: { nodes: any; edges: any }, selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number } position: { x: number; y: number; paneX?: number; paneY?: number }
@ -73,6 +75,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const { templates, reactFlowInstance } = useContext(typesContext); const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null); const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
const [tabsState, setTabsState] = useState<TabsState>({}); const [tabsState, setTabsState] = useState<TabsState>({});
const [getTweak, setTweak] = useState({});
const newNodeId = useRef(uid()); const newNodeId = useRef(uid());
function incrementNodeId() { function incrementNodeId() {
@ -198,10 +201,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
edge.style = { stroke: "#555555" }; edge.style = { stroke: "#555555" };
}); });
} }
function updateDisplay_name(node: NodeType, template: APIClassType) { function updateDisplay_name(node: NodeType, template: APIClassType) {
node.data.node.display_name = template["display_name"] node.data.node.display_name = template["display_name"] || node.data.type;
? template["display_name"] }
: node.data.type;
function updateNodeDocumentation(node: NodeType, template: APIClassType) {
node.data.node.documentation = template["documentation"];
} }
function processFlowNodes(flow) { function processFlowNodes(flow) {
@ -218,6 +224,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
updateNodeEdges(flow, node, template); updateNodeEdges(flow, node, template);
updateNodeDescription(node, template); updateNodeDescription(node, template);
updateNodeTemplate(node, template); updateNodeTemplate(node, template);
updateNodeDocumentation(node, template);
} }
}); });
} }
@ -268,16 +275,20 @@ export function TabsProvider({ children }: { children: ReactNode }) {
/** /**
* Downloads the current flow as a JSON file * Downloads the current flow as a JSON file
*/ */
function downloadFlow(flow: FlowType) { function downloadFlow(
flow: FlowType,
flowName: string,
flowDescription?: string
) {
// create a data URI with the current flow data // create a data URI with the current flow data
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
JSON.stringify(flow) JSON.stringify({ ...flow, name: flowName, description: flowDescription })
)}`; )}`;
// create a link element and set its properties // create a link element and set its properties
const link = document.createElement("a"); const link = document.createElement("a");
link.href = jsonString; link.href = jsonString;
link.download = `${flows.find((f) => f.id === tabId).name}.json`; link.download = `${flowName}.json`;
// simulate a click on the link element to trigger the download // simulate a click on the link element to trigger the download
link.click(); link.click();
@ -410,7 +421,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
y: insidePosition.y + n.position.y - minimumY, y: insidePosition.y + n.position.y - minimumY,
}, },
data: { data: {
...n.data, ..._.cloneDeep(n.data),
id: newId, id: newId,
}, },
}; };
@ -640,6 +651,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
tabsState, tabsState,
setTabsState, setTabsState,
paste, paste,
getTweak,
setTweak,
}} }}
> >
{children} {children}

View file

@ -311,7 +311,7 @@ export async function getBuildStatus(
export async function postBuildInit( export async function postBuildInit(
flow: FlowType flow: FlowType
): Promise<AxiosResponse<InitTypeAPI>> { ): Promise<AxiosResponse<InitTypeAPI>> {
return await axios.post(`/api/v1/build/init`, flow); return await axios.post(`/api/v1/build/init/${flow.id}`, flow);
} }
// fetch(`/upload/${id}`, { // fetch(`/upload/${id}`, {

View file

@ -0,0 +1,364 @@
/**
* Default description for the flow
* @constant
*/
export const DESCRIPTIONS: string[] = [
"Chain the Words, Master Language!",
"Language Architect at Work!",
"Empowering Language Engineering.",
"Craft Language Connections Here.",
"Create, Connect, Converse.",
"Smart Chains, Smarter Conversations.",
"Bridging Prompts for Brilliance.",
"Language Models, Unleashed.",
"Your Hub for Text Generation.",
"Promptly Ingenious!",
"Building Linguistic Labyrinths.",
"Create, Chain, Communicate.",
"Connect the Dots, Craft Language.",
"Interactive Language Weaving.",
"Generate, Innovate, Communicate.",
"Conversation Catalyst Engine.",
"Language Chainlink Master.",
"Design Dialogues with LangFlow.",
"Nurture NLP Nodes Here.",
"Conversational Cartography Unlocked.",
"Design, Develop, Dialogize.",
"Unleashing Linguistic Creativity.",
"Graph Your Way to Great Conversations.",
"The Power of Language at Your Fingertips.",
"Sculpting Language with Precision.",
"Where Language Meets Logic.",
"Building Intelligent Interactions.",
"Your Passport to Linguistic Landscapes.",
"Create, Curate, Communicate with LangFlow.",
"Flow into the Future of Language.",
"Mapping Meaningful Conversations.",
"Unravel the Art of Articulation.",
"Language Engineering Excellence.",
"Navigate the Networks of Conversation.",
"Crafting Conversations, One Node at a Time.",
"The Pinnacle of Prompt Generation.",
"Language Models, Mapped and Mastered.",
"Powerful Prompts, Perfectly Positioned.",
"Innovation in Interaction with LangFlow.",
"Your Toolkit for Text Generation.",
"Unfolding Linguistic Possibilities.",
"Building Powerful Solutions with Language Models.",
"Uncover Business Opportunities with NLP.",
"Harness the Power of Conversational AI.",
"Transform Your Business with Smart Dialogues.",
"Craft Meaningful Interactions, Generate Value.",
"Unleashing Business Potential through Language Engineering.",
"Empowering Enterprises with Intelligent Interactions.",
"Driving Innovation in Business Communication.",
"Catalyzing Business Growth through Conversational AI.",
"Text Generation Meets Business Transformation.",
"Navigate the Linguistic Landscape, Discover Opportunities.",
"Create Powerful Connections, Boost Business Value.",
"Empowering Communication, Enabling Opportunities.",
"Advanced NLP for Groundbreaking Business Solutions.",
"Innovation in Interaction, Revolution in Revenue.",
"Maximize Impact with Intelligent Conversations.",
"Beyond Text Generation - Unleashing Business Opportunities.",
"Unlock the Power of AI in Your Business Conversations.",
"Crafting Dialogues that Drive Business Success.",
"Engineered for Excellence, Built for Business.",
];
/**
* Adjectives for the name of the flow
* @constant
*
*/
export const ADJECTIVES: string[] = [
"admiring",
"adoring",
"agitated",
"amazing",
"angry",
"awesome",
"backstabbing",
"berserk",
"big",
"boring",
"clever",
"cocky",
"compassionate",
"condescending",
"cranky",
"desperate",
"determined",
"distracted",
"dreamy",
"drunk",
"ecstatic",
"elated",
"elegant",
"evil",
"fervent",
"focused",
"furious",
"gigantic",
"gloomy",
"goofy",
"grave",
"happy",
"high",
"hopeful",
"hungry",
"insane",
"jolly",
"jovial",
"kickass",
"lonely",
"loving",
"mad",
"modest",
"naughty",
"nauseous",
"nostalgic",
"pedantic",
"pensive",
"prickly",
"reverent",
"romantic",
"sad",
"serene",
"sharp",
"sick",
"silly",
"sleepy",
"small",
"stoic",
"stupefied",
"suspicious",
"tender",
"thirsty",
"tiny",
"trusting",
"bubbly",
"charming",
"cheerful",
"comical",
"dazzling",
"delighted",
"dynamic",
"effervescent",
"enthusiastic",
"exuberant",
"fluffy",
"friendly",
"funky",
"giddy",
"giggly",
"gleeful",
"goofy",
"graceful",
"grinning",
"hilarious",
"inquisitive",
"joyous",
"jubilant",
"lively",
"mirthful",
"mischievous",
"optimistic",
"peppy",
"perky",
"playful",
"quirky",
"radiant",
"sassy",
"silly",
"spirited",
"sprightly",
"twinkly",
"upbeat",
"vibrant",
"witty",
"zany",
"zealous",
];
/**
* Nouns for the name of the flow
* @constant
*
*/
export const NOUNS: string[] = [
"albattani",
"allen",
"almeida",
"archimedes",
"ardinghelli",
"aryabhata",
"austin",
"babbage",
"banach",
"bardeen",
"bartik",
"bassi",
"bell",
"bhabha",
"bhaskara",
"blackwell",
"bohr",
"booth",
"borg",
"bose",
"boyd",
"brahmagupta",
"brattain",
"brown",
"carson",
"chandrasekhar",
"colden",
"cori",
"cray",
"curie",
"darwin",
"davinci",
"dijkstra",
"dubinsky",
"easley",
"einstein",
"elion",
"engelbart",
"euclid",
"euler",
"fermat",
"fermi",
"feynman",
"franklin",
"galileo",
"gates",
"goldberg",
"goldstine",
"goldwasser",
"golick",
"goodall",
"hamilton",
"hawking",
"heisenberg",
"heyrovsky",
"hodgkin",
"hoover",
"hopper",
"hugle",
"hypatia",
"jang",
"jennings",
"jepsen",
"joliot",
"jones",
"kalam",
"kare",
"keller",
"khorana",
"kilby",
"kirch",
"knuth",
"kowalevski",
"lalande",
"lamarr",
"leakey",
"leavitt",
"lichterman",
"liskov",
"lovelace",
"lumiere",
"mahavira",
"mayer",
"mccarthy",
"mcclintock",
"mclean",
"mcnulty",
"meitner",
"meninsky",
"mestorf",
"minsky",
"mirzakhani",
"morse",
"murdock",
"newton",
"nobel",
"noether",
"northcutt",
"noyce",
"panini",
"pare",
"pasteur",
"payne",
"perlman",
"pike",
"poincare",
"poitras",
"ptolemy",
"raman",
"ramanujan",
"ride",
"ritchie",
"roentgen",
"rosalind",
"saha",
"sammet",
"shaw",
"shirley",
"shockley",
"sinoussi",
"snyder",
"spence",
"stallman",
"stonebraker",
"swanson",
"swartz",
"swirles",
"tesla",
"thompson",
"torvalds",
"turing",
"varahamihira",
"visvesvaraya",
"volhard",
"wescoff",
"williams",
"wilson",
"wing",
"wozniak",
"wright",
"yalow",
"yonath",
"coulomb",
"degrasse",
"dewey",
"edison",
"eratosthenes",
"faraday",
"galton",
"gauss",
"herschel",
"hubble",
"joule",
"kaku",
"kepler",
"khayyam",
"lavoisier",
"maxwell",
"mendel",
"mendeleev",
"ohm",
"pascal",
"planck",
"riemann",
"schrodinger",
"sagan",
"tesla",
"tyson",
"volta",
"watt",
"weber",
"wien",
"zoBell",
"zuse",
"carroll",
];

View file

@ -0,0 +1,9 @@
import React, { forwardRef } from "react";
import { ReactComponent as VertexAISVG } from "./vertex_ai.svg";
export const VertexAIIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
return <VertexAISVG ref={ref} {...props} />;
});

View file

@ -0,0 +1 @@
<svg viewBox="0 0 32 32" fit="" height="100%" width="100%" preserveAspectRatio="xMidYMid meet" focusable="false"><path d="M26.69 18.53a1 1 0 00-1.4-.22L16 25.17v.29a1 1 0 110 1.91v.05a1 1 0 00.6-.19l9.88-7.3a1 1 0 00.21-1.4z" fill="#80868b"></path><path d="M16 27.37a1 1 0 110-1.91v-.29l-9.29-6.86a1 1 0 00-1.4.22 1 1 0 00.21 1.4l9.89 7.3a1 1 0 00.59.19v-.05z" fill="#9ba0a5"></path><path d="M16 24.46a2 2 0 102 2 2 2 0 00-2-2zm0 2.91a1 1 0 111-.95 1 1 0 01-1 .95z" fill="#606368"></path><path d="M8 8.14a1 1 0 01-1-1V4.63a1 1 0 112 0v2.51a1 1 0 01-1 1z" fill="#9ba0a5"></path><circle cx="7.97" cy="16" r="1.01" fill="#9ba0a5"></circle><circle cx="7.97" cy="13.05" r="1.01" fill="#9ba0a5"></circle><circle cx="7.97" cy="10.09" r="1.01" fill="#9ba0a5"></circle><path d="M24 11.07a1 1 0 01-1-1V7.55a1 1 0 012 0v2.52a1 1 0 01-1 1z" fill="#606368"></path><circle cx="24.03" cy="16.01" r="1.01" fill="#606368"></circle><circle cx="24.03" cy="13.02" r="1.01" fill="#606368"></circle><circle cx="24.03" cy="4.63" r="1.01" fill="#606368"></circle><path d="M16 20a1 1 0 01-1-1v-2.54a1 1 0 012 0V19a1 1 0 01-1 1z" fill="#80868b"></path><circle cx="16" cy="21.93" r="1.01" fill="#80868b"></circle><circle cx="16" cy="13.51" r="1.01" fill="#80868b"></circle><circle cx="16" cy="10.56" r="1.01" fill="#80868b"></circle><path d="M20 14.05a1 1 0 01-1-1v-2.51a1 1 0 112 0v2.51a1 1 0 01-1 1z" fill="#606368"></path><circle cx="20.02" cy="7.58" r="1.01" fill="#606368"></circle><circle cx="20.02" cy="18.92" r="1.01" fill="#606368"></circle><circle cx="20.02" cy="15.97" r="1.01" fill="#606368"></circle><circle cx="11.98" cy="18.92" r="1.01" fill="#9ba0a5"></circle><circle cx="11.98" cy="10.56" r="1.01" fill="#9ba0a5"></circle><circle cx="11.98" cy="7.58" r="1.01" fill="#9ba0a5"></circle><path d="M12 17a1 1 0 01-1-1v-2.54a1 1 0 012 0V16a1 1 0 01-1 1z" fill="#9ba0a5"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,4 +1,4 @@
import { useContext, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
import "ace-builds/src-noconflict/mode-python"; import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github"; import "ace-builds/src-noconflict/theme-github";
@ -26,14 +26,42 @@ import {
TabsTrigger, TabsTrigger,
} from "../../components/ui/tabs"; } from "../../components/ui/tabs";
import { Check, Clipboard, Code2 } from "lucide-react"; import { Check, Clipboard, Code2 } from "lucide-react";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import { buildTweaks, classNames, limitScrollFieldsModal } from "../../utils";
import AccordionComponent from "../../components/AccordionComponent";
import CodeAreaComponent from "../../components/codeAreaComponent";
import Dropdown from "../../components/dropdownComponent";
import FloatComponent from "../../components/floatComponent";
import InputComponent from "../../components/inputComponent";
import InputFileComponent from "../../components/inputFileComponent";
import InputListComponent from "../../components/inputListComponent";
import IntComponent from "../../components/intComponent";
import PromptAreaComponent from "../../components/promptComponent";
import TextAreaComponent from "../../components/textAreaComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import { cloneDeep, filter } from "lodash";
import { TabsContext } from "../../contexts/tabsContext";
export default function ApiModal({ flow }: { flow: FlowType }) { export default function ApiModal({ flow }: { flow: FlowType }) {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
const { closePopUp } = useContext(PopUpContext); const { closePopUp, closeEdit, setCloseEdit } = useContext(PopUpContext);
const [activeTab, setActiveTab] = useState("0"); const [activeTab, setActiveTab] = useState("0");
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const [enabled, setEnabled] = useState(null);
const [openAccordion, setOpenAccordion] = useState([]);
const tweak = useRef([]);
const tweaksList = useRef([]);
const { setTweak, getTweak } = useContext(TabsContext);
const copyToClipboard = () => { const copyToClipboard = () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) { if (!navigator.clipboard || !navigator.clipboard.writeText) {
return; return;
@ -47,18 +75,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
}, 2000); }, 2000);
}); });
}; };
function setModalOpen(x: boolean) { const pythonApiCode = getPythonApiCode(flow, tweak.current);
setOpen(x); const curl_code = getCurlCode(flow, tweak.current);
if (x === false) { const pythonCode = getPythonCode(flow, tweak.current);
closePopUp(); const tweaksCode = buildTweaks(flow);
}
}
const pythonApiCode = getPythonApiCode(flow);
const curl_code = getCurlCode(flow);
const pythonCode = getPythonCode(flow);
const tabs = [ const tabs = [
{ {
name: "cURL", name: "cURL",
@ -80,10 +100,169 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
code: pythonCode, code: pythonCode,
}, },
]; ];
useEffect(() => {
if (closeEdit !== "") {
tweak.current = getTweak;
if (tweak.current.length > 0) {
setActiveTab("3");
openAccordions();
} else {
startTweaks();
}
} else {
startTweaks();
}
}, [closeEdit]);
useEffect(() => {
filterNodes();
}, []);
if (Object.keys(tweaksCode).length > 0) {
tabs.push({
name: "Tweaks",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
code: pythonCode,
});
}
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
setCloseEdit("");
setTweak([]);
closePopUp();
}
}
function startTweaks() {
tweak.current.push(buildTweaks(flow));
}
function filterNodes() {
let arrNodesWithValues = [];
flow["data"]["nodes"].forEach((t) => {
Object.keys(t["data"]["node"]["template"])
.filter(
(n) =>
n.charAt(0) !== "_" &&
t.data.node.template[n].show &&
(t.data.node.template[n].type === "str" ||
t.data.node.template[n].type === "bool" ||
t.data.node.template[n].type === "float" ||
t.data.node.template[n].type === "code" ||
t.data.node.template[n].type === "prompt" ||
t.data.node.template[n].type === "file" ||
t.data.node.template[n].type === "int")
)
.map((n, i) => {
arrNodesWithValues.push(t["id"]);
});
});
tweaksList.current = arrNodesWithValues.filter((value, index, self) => {
return self.indexOf(value) === index;
});
}
function buildTweakObject(tw, changes, template) {
if (template.type === "float") {
changes = parseFloat(changes);
}
if (template.type === "int") {
changes = parseInt(changes);
}
if (template.list === true && Array.isArray(changes)) {
changes = changes?.filter((x) => x !== "");
}
const existingTweak = tweak.current.find((element) =>
element.hasOwnProperty(tw)
);
if (existingTweak) {
existingTweak[tw][template["name"]] = changes;
if (existingTweak[tw][template["name"]] == template.value) {
tweak.current.forEach((element) => {
if (element[tw] && Object.keys(element[tw])?.length === 0) {
tweak.current = tweak.current.filter((obj) => {
const prop = obj[Object.keys(obj)[0]].prop;
return prop !== undefined && prop !== null && prop !== "";
});
}
});
}
} else {
const newTweak = {
[tw]: {
[template["name"]]: changes,
},
};
tweak.current.push(newTweak);
}
const pythonApiCode = getPythonApiCode(flow, tweak.current);
const curl_code = getCurlCode(flow, tweak.current);
const pythonCode = getPythonCode(flow, tweak.current);
tabs[0].code = curl_code;
tabs[1].code = pythonApiCode;
tabs[2].code = pythonCode;
setTweak(tweak.current);
}
function buildContent(value) {
const htmlContent = (
<div className="w-[200px]">
<span>{value != null && value != "" ? value : "None"}</span>
</div>
);
return htmlContent;
}
function getValue(value, node, template) {
let returnValue = value ?? "";
if (getTweak.length > 0) {
for (const obj of getTweak) {
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (key == node["id"]) {
Object.keys(value).forEach((key) => {
if (key == template["name"]) {
returnValue = value[key];
}
});
}
});
}
} else {
return value ?? "";
}
return returnValue;
}
function openAccordions() {
let accordionsToOpen = [];
tweak.current.forEach((el) => {
Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key);
setOpenAccordion(accordionsToOpen);
}
});
});
}
return ( return (
<Dialog open={true} onOpenChange={setModalOpen}> <Dialog open={true} onOpenChange={setModalOpen}>
<DialogTrigger></DialogTrigger> <DialogTrigger></DialogTrigger>
<DialogContent className="h-[580px] sm:max-w-[600px] lg:max-w-[800px]"> <DialogContent className="h-[580px] sm:max-w-[700px] lg:max-w-[850px]">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center"> <DialogTitle className="flex items-center">
<span className="pr-2">Code</span> <span className="pr-2">Code</span>
@ -93,9 +272,14 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
</DialogHeader> </DialogHeader>
<Tabs <Tabs
defaultValue={"0"} value={activeTab}
className="h-full w-full overflow-hidden rounded-md border bg-muted text-center" className="h-full w-full overflow-hidden rounded-md border bg-muted text-center"
onValueChange={(value) => setActiveTab(value)} onValueChange={(value) => {
setActiveTab(value);
if (value === "3") {
openAccordions();
}
}}
> >
<div className="flex items-center justify-between px-2"> <div className="flex items-center justify-between px-2">
<TabsList> <TabsList>
@ -120,14 +304,448 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
<TabsContent <TabsContent
value={index.toString()} value={index.toString()}
className="-mt-1 h-full w-full overflow-hidden px-4 pb-4" className="-mt-1 h-full w-full overflow-hidden px-4 pb-4"
key={index} // Remember to add a unique key prop
> >
<SyntaxHighlighter {index < 3 ? (
className="h-[400px] w-full overflow-auto" <SyntaxHighlighter
language={tab.mode} className="h-[400px] w-full overflow-auto"
style={oneDark} language={tab.mode}
> style={oneDark}
{tab.code} >
</SyntaxHighlighter> {tab.code}
</SyntaxHighlighter>
) : index === 3 ? (
<>
<div className="mt-2 flex h-[400px] w-full">
<div
className={classNames(
"w-full rounded-lg border-[1px] border-gray-200 bg-muted",
1 == 1
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
{flow["data"]["nodes"].map((t: any, index) => (
<div className="px-3" key={index}>
{tweaksList.current.includes(t["data"]["id"]) && (
<AccordionComponent
trigger={t["data"]["id"]}
open={openAccordion}
>
<div className="flex h-fit flex-col gap-5">
<Table className="table-fixed bg-muted outline-1">
<TableHeader className="h-10 border-gray-200 text-xs font-medium text-gray-500">
<TableRow className="dark:border-b-muted">
<TableHead className="h-7 text-center">
PARAM
</TableHead>
<TableHead className="h-7 p-0 text-center">
VALUE
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(t["data"]["node"]["template"])
.filter(
(n) =>
n.charAt(0) !== "_" &&
t.data.node.template[n].show &&
(t.data.node.template[n].type ===
"str" ||
t.data.node.template[n].type ===
"bool" ||
t.data.node.template[n].type ===
"float" ||
t.data.node.template[n].type ===
"code" ||
t.data.node.template[n].type ===
"prompt" ||
t.data.node.template[n].type ===
"file" ||
t.data.node.template[n].type ===
"int")
)
.map((n, i) => {
//console.log(t.data.node.template[n]);
return (
<TableRow
key={i}
className="h-10 dark:border-b-muted"
>
<TableCell className="p-0 text-center text-sm text-gray-900">
{n}
</TableCell>
<TableCell className="p-0 text-center text-xs text-gray-900 dark:text-gray-300">
<div className="m-auto w-[250px]">
{t.data.node.template[n]
.type === "str" &&
!t.data.node.template[n]
.options ? (
<div className="mx-auto">
{t.data.node.template[n]
.list ? (
<InputListComponent
editNode={true}
disabled={false}
value={
!t.data.node.template[
n
].value ||
t.data.node.template[
n
].value === ""
? [""]
: t.data.node
.template[n]
.value
}
onChange={(k) => {}}
onAddInput={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
) : t.data.node.template[n]
.multiline ? (
<ShadTooltip
delayDuration={1000}
content={buildContent(
t.data.node.template[
n
].value
)}
>
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node
.template[n]
.value,
t.data,
t.data.node
.template[n]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
t.data.node.template[
n
].password ?? false
}
value={getValue(
t.data.node.template[
n
].value,
t.data,
t.data.node.template[
n
]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
)}
</div>
) : t.data.node.template[n]
.type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
enabled={
t.data.node.template[n]
.value
}
setEnabled={(e) => {
t.data.node.template[
n
].value = e;
setEnabled(e);
buildTweakObject(
t["data"]["id"],
e,
t.data.node.template[
n
]
);
}}
size="small"
disabled={false}
/>
</div>
) : t.data.node.template[n]
.type === "file" ? (
<ShadTooltip
delayDuration={1000}
content={buildContent(
getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={false}
value={
t.data.node.template[
n
].value ?? ""
}
onChange={(
k: any
) => {}}
fileTypes={
t.data.node.template[
n
].fileTypes
}
suffixes={
t.data.node.template[
n
].suffixes
}
onFileChange={(
k: any
) => {}}
></InputFileComponent>
</div>
</ShadTooltip>
) : t.data.node.template[n]
.type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[
n
]
);
}}
/>
</div>
) : t.data.node.template[n]
.type === "str" &&
t.data.node.template[n]
.options ? (
<div className="mx-auto">
<Dropdown
editNode={true}
apiModal={true}
options={
t.data.node.template[n]
.options
}
onSelect={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[
n
]
);
}}
value={getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)}
></Dropdown>
</div>
) : t.data.node.template[n]
.type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[
n
]
);
}}
/>
</div>
) : t.data.node.template[n]
.type === "prompt" ? (
<ShadTooltip
delayDuration={1000}
content={buildContent(
getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<PromptAreaComponent
editNode={true}
disabled={false}
value={getValue(
t.data.node.template[
n
].value,
t.data,
t.data.node.template[
n
]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n]
.type === "code" ? (
<ShadTooltip
delayDuration={1000}
content={buildContent(
getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
)
)}
>
<div className="mx-auto">
<CodeAreaComponent
disabled={false}
editNode={true}
value={getValue(
t.data.node.template[
n
].value,
t.data,
t.data.node.template[
n
]
)}
onChange={(k) => {
buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n]
.type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</AccordionComponent>
)}
{tweaksList.current.length === 0 && (
<>
<div className="pt-3">
No tweaks are available for this flow.
</div>
</>
)}
</div>
))}
{/*
<div className="flex flex-col gap-5 bg-muted">
<Table className="table-fixed bg-muted outline-1">
<TableHeader className="border-gray-200 text-gray-500 text-xs font-medium h-10">
<TableRow className="dark:border-b-muted">
<TableHead className="h-5 text-center">
TWEAK
</TableHead>
<TableHead className="p-0 h-5 text-center">
VALUE
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((invoice) => (
<TableRow className="p-0 text-center text-gray-900 text-sm">
<TableCell className="p-2 text-center text-gray-900 text-sm truncate">
{invoice.paymentStatus}
</TableCell>
<TableCell className="p-2 text-center text-gray-900 text-sm truncate">
{invoice.paymentMethod}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div> */}
</div>
</div>
</>
) : null}
</TabsContent> </TabsContent>
))} ))}
</Tabs> </Tabs>

View file

@ -6,8 +6,7 @@ import {
classNames, classNames,
limitScrollFieldsModal, limitScrollFieldsModal,
nodeColors, nodeColors,
nodeIcons, nodeIconsLucide,
toNormalCase,
toTitleCase, toTitleCase,
} from "../../utils"; } from "../../utils";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
@ -28,7 +27,7 @@ export default function NodeModal({ data }: { data: NodeDataType }) {
} }
} }
// any to avoid type conflict // any to avoid type conflict
const Icon: any = nodeIcons[types[data.type]]; const Icon: any = nodeIconsLucide[types[data.type]];
return ( return (
<Transition.Root show={open} appear={true} as={Fragment}> <Transition.Root show={open} appear={true} as={Fragment}>
<Dialog <Dialog

View file

@ -11,7 +11,7 @@ import remarkMath from "remark-math";
import { CodeBlock } from "./codeBlock"; import { CodeBlock } from "./codeBlock";
import Convert from "ansi-to-html"; import Convert from "ansi-to-html";
import { User2, MessageCircle } from "lucide-react"; import { User2, MessageCircle } from "lucide-react";
import DOMPurify from "dompurify";
export default function ChatMessage({ export default function ChatMessage({
chat, chat,
lockChat, lockChat,
@ -77,7 +77,7 @@ export default function ChatMessage({
className=" ml-3 inline-block h-full w-[95%] cursor-pointer overflow-scroll rounded-md className=" ml-3 inline-block h-full w-[95%] cursor-pointer overflow-scroll rounded-md
border border-ring bg-muted px-2 pb-3 pt-3 text-start text-muted-foreground scrollbar-hide" border border-ring bg-muted px-2 pb-3 pt-3 text-start text-muted-foreground scrollbar-hide"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: convert.toHtml(chat.thought), __html: DOMPurify.sanitize(convert.toHtml(chat.thought)),
}} }}
></div> ></div>
)} )}
@ -148,12 +148,12 @@ export default function ChatMessage({
) : ( ) : (
<div className="flex w-full items-center"> <div className="flex w-full items-center">
<div className="inline-block px-3 text-start text-muted-foreground "> <div className="inline-block px-3 text-start text-muted-foreground ">
<span {message.split("\n").map((line, index) => (
className="text-muted-foreground " <span key={index} className="text-muted-foreground ">
dangerouslySetInnerHTML={{ {line}
__html: message.replace(/\n/g, "<br>"), <br />
}} </span>
></span> ))}
</div> </div>
</div> </div>
)} )}

View file

@ -11,6 +11,7 @@ import { ChatMessageType } from "../../types/chat";
import ChatInput from "./chatInput"; import ChatInput from "./chatInput";
import _ from "lodash"; import _ from "lodash";
import { getHealth } from "../../controllers/API";
export default function ChatModal({ export default function ChatModal({
flow, flow,
@ -204,25 +205,27 @@ export default function ChatModal({
handleOnClose(event); handleOnClose(event);
}; };
newWs.onerror = (ev) => { newWs.onerror = (ev) => {
console.log(ev, "error"); getHealth()
if (flow.id === "") { .then((res) => {
connectWS(); if (res.status === 200) {
} else { connectWS();
setErrorData({ }
title: "There was an error on web connection, please: ", })
list: [ .catch((err) => {
"Refresh the page", setErrorData({
"Use a new flow tab", // message when the backend failed
"Check if the backend is up", title: "The backend is not responding. Please try again later.",
], // possible solution list
list: [
"Check your internet connection.",
"Check if the backend is running.",
],
});
}); });
}
}; };
ws.current = newWs; ws.current = newWs;
} catch (error) { } catch (error) {
if (flow.id === "") { connectWS();
connectWS();
}
console.log(error); console.log(error);
} }
} }

View file

@ -34,12 +34,13 @@ export default function CodeAreaModal({
const [code, setCode] = useState(value); const [code, setCode] = useState(value);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);
const { closePopUp } = useContext(PopUpContext); const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef(); const ref = useRef();
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
if (x === false) { if (x === false) {
setTimeout(() => { setTimeout(() => {
setCloseEdit("editcode");
closePopUp(); closePopUp();
}, 300); }, 300);
} }

View file

@ -23,7 +23,8 @@ export default function ExportModal() {
const { closePopUp } = useContext(PopUpContext); const { closePopUp } = useContext(PopUpContext);
const ref = useRef(); const ref = useRef();
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext); const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
useContext(TabsContext);
const [isMaxLength, setIsMaxLength] = useState(false); const [isMaxLength, setIsMaxLength] = useState(false);
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
@ -80,9 +81,18 @@ export default function ExportModal() {
<DialogFooter> <DialogFooter>
<Button <Button
onClick={() => { onClick={() => {
if (checked) downloadFlow(flows.find((f) => f.id === tabId)); if (checked)
downloadFlow(
flows.find((f) => f.id === tabId),
name,
description
);
else else
downloadFlow(removeApiKeys(flows.find((f) => f.id === tabId))); downloadFlow(
removeApiKeys(flows.find((f) => f.id === tabId)),
name,
description
);
closePopUp(); closePopUp();
}} }}

View file

@ -38,11 +38,12 @@ export default function GenericModal({
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);
const { closePopUp } = useContext(PopUpContext); const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef(); const ref = useRef();
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
if (x === false) { if (x === false) {
setCloseEdit("generic");
closePopUp(); closePopUp();
} }
} }

View file

@ -16,12 +16,13 @@ export default function PromptAreaModal({
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { dark } = useContext(darkContext); const { dark } = useContext(darkContext);
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);
const { closePopUp } = useContext(PopUpContext); const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef(); const ref = useRef();
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
if (x === false) { if (x === false) {
setTimeout(() => { setTimeout(() => {
setCloseEdit("prompt");
closePopUp(); closePopUp();
}, 300); }, 300);
} }

View file

@ -15,12 +15,13 @@ export default function TextAreaModal({
}) { }) {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const [myValue, setMyValue] = useState(value); const [myValue, setMyValue] = useState(value);
const { closePopUp } = useContext(PopUpContext); const { closePopUp, setCloseEdit } = useContext(PopUpContext);
const ref = useRef(); const ref = useRef();
function setModalOpen(x: boolean) { function setModalOpen(x: boolean) {
setOpen(x); setOpen(x);
if (x === false) { if (x === false) {
setTimeout(() => { setTimeout(() => {
setCloseEdit("textarea");
closePopUp(); closePopUp();
}, 300); }, 300);
} }

View file

@ -62,7 +62,7 @@ export default function ExtraSidebar() {
return ( return (
<div className="flex h-full w-52 flex-col overflow-hidden border-r scrollbar-hide"> <div className="flex h-full w-52 flex-col overflow-hidden border-r scrollbar-hide">
<div className="mb-2 mt-2 flex w-full items-center justify-between gap-2 px-2"> <div className="mb-2 mt-2 flex w-full items-center justify-between gap-2 px-2">
<ShadTooltip delayDuration={1000} content="Import" side="top"> <ShadTooltip content="Import" side="top">
<button <button
className="relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted" className="relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted"
onClick={() => { onClick={() => {
@ -74,7 +74,7 @@ export default function ExtraSidebar() {
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Export" side="top"> <ShadTooltip content="Export" side="top">
<button <button
className={classNames( className={classNames(
"relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted" "relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted"
@ -86,7 +86,7 @@ export default function ExtraSidebar() {
<FileDown strokeWidth={1.5} className="h-5 w-5 "></FileDown> <FileDown strokeWidth={1.5} className="h-5 w-5 "></FileDown>
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Code" side="top"> <ShadTooltip content="Code" side="top">
<button <button
className={classNames( className={classNames(
"relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted" "relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted"
@ -99,7 +99,7 @@ export default function ExtraSidebar() {
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Save" side="top"> <ShadTooltip content="Save" side="top">
<button <button
className="relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted" className="relative inline-flex w-full items-center justify-center rounded-md bg-background px-2 py-2 text-foreground shadow-sm ring-1 ring-inset ring-input transition-all duration-500 ease-in-out hover:bg-muted"
onClick={(event) => { onClick={(event) => {
@ -155,7 +155,6 @@ export default function ExtraSidebar() {
.map((t: string, k) => ( .map((t: string, k) => (
<ShadTooltip <ShadTooltip
content={data[d][t].display_name} content={data[d][t].display_name}
delayDuration={1500}
side="right" side="right"
key={data[d][t].display_name} key={data[d][t].display_name}
> >

View file

@ -1,5 +1,5 @@
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { Settings2, Copy, Trash2, Menu } from "lucide-react"; import { Settings2, Copy, Trash2, FileText } from "lucide-react";
import { classNames } from "../../../../utils"; import { classNames } from "../../../../utils";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import { useReactFlow } from "reactflow"; import { useReactFlow } from "reactflow";
@ -30,23 +30,21 @@ const NodeToolbarComponent = (props) => {
<> <>
<div className="w-26 h-10"> <div className="w-26 h-10">
<span className="isolate inline-flex rounded-md shadow-sm"> <span className="isolate inline-flex rounded-md shadow-sm">
<ShadTooltip delayDuration={1000} content="Delete" side="top"> <ShadTooltip content="Delete" side="top">
<button <button
className="relative inline-flex items-center rounded-l-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" className="relative inline-flex items-center rounded-l-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
onClick={() => { onClick={() => {
props.deleteNode(props.data.id); props.deleteNode(props.data.id);
}} }}
> >
<Trash2 className="h-4 w-4 "></Trash2> <Trash2 className="h-4 w-4"></Trash2>
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Duplicate" side="top"> <ShadTooltip content="Duplicate" side="top">
<button <button
className={classNames( className={classNames(
nodeLength > 0 "relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
? "relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
: "relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
)} )}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
@ -65,26 +63,62 @@ const NodeToolbarComponent = (props) => {
); );
}} }}
> >
<Copy className="h-4 w-4 "></Copy> <Copy className="h-4 w-4"></Copy>
</button> </button>
</ShadTooltip> </ShadTooltip>
{nodeLength > 0 && ( <ShadTooltip
<ShadTooltip delayDuration={1000} content="Edit" side="top"> content={
<button props.data.node.documentation === ""
className="relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" ? "Coming Soon"
onClick={(event) => { : "Documentation"
}
side="top"
>
<a
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out 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(); event.preventDefault();
props.openPopUp(<EditNodeModal data={props.data} />); }
}} }}
> >
<Settings2 className="h-4 w-4 "></Settings2> <FileText className="h-4 w-4 "></FileText>
</button> </a>
</ShadTooltip> </ShadTooltip>
)}
{/* <Menu as="div" className="relative inline-block text-left z-100"> <ShadTooltip content="Edit" side="top">
<button className="text-gray-700 transition-all duration-500 ease-in-out shadow-md relative -ml-px inline-flex items-center bg-background px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md"> <button
className={classNames(
"relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
(nodeLength == 0
? " text-muted-foreground"
: " text-foreground")
)}
onClick={(event) => {
if (nodeLength == 0) {
event.preventDefault();
}
event.preventDefault();
props.openPopUp(<EditNodeModal data={props.data} />);
}}
>
<Settings2 className="h-4 w-4 "></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">
<div> <div>
<Menu.Button className="flex items-center"> <Menu.Button className="flex items-center">
<EllipsisVerticalIcon <EllipsisVerticalIcon
@ -117,7 +151,7 @@ const NodeToolbarComponent = (props) => {
className={classNames( className={classNames(
active active
? "bg-muted text-gray-900" ? "bg-muted text-gray-900"
: "text-gray-700", : "text-foreground",
"w-full group flex items-center px-4 py-2 text-sm" "w-full group flex items-center px-4 py-2 text-sm"
)} )}
> >
@ -157,7 +191,7 @@ const NodeToolbarComponent = (props) => {
className={classNames( className={classNames(
active active
? "bg-muted text-gray-900" ? "bg-muted text-gray-900"
: "text-gray-700", : "text-foreground",
"w-full group flex items-center px-4 py-2 text-sm" "w-full group flex items-center px-4 py-2 text-sm"
)} )}
> >

View file

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

View file

@ -1,10 +1,4 @@
import { import { ReactElement, ReactNode } from "react";
ComponentType,
ForwardRefExoticComponent,
ReactElement,
ReactNode,
SVGProps,
} from "react";
import { NodeDataType } from "../flow/index"; import { NodeDataType } from "../flow/index";
import { typesContextType } from "../typesContext"; import { typesContextType } from "../typesContext";
export type InputComponentType = { export type InputComponentType = {
@ -28,6 +22,7 @@ export type DropDownComponentType = {
options: string[]; options: string[];
onSelect: (value: string) => void; onSelect: (value: string) => void;
editNode?: boolean; editNode?: boolean;
apiModal?: boolean;
numberOfOptions?: number; numberOfOptions?: number;
}; };
export type ParameterComponentType = { export type ParameterComponentType = {
@ -41,12 +36,14 @@ export type ParameterComponentType = {
name?: string; name?: string;
tooltipTitle: string; tooltipTitle: string;
dataContext?: typesContextType; dataContext?: typesContextType;
info?: string;
}; };
export type InputListComponentType = { export type InputListComponentType = {
value: string[]; value: string[];
onChange: (value: string[]) => void; onChange: (value: string[]) => void;
disabled: boolean; disabled: boolean;
editNode?: boolean; editNode?: boolean;
onAddInput?: (value?: string[]) => void;
}; };
export type TextAreaComponentType = { export type TextAreaComponentType = {
@ -115,3 +112,17 @@ export type RadialProgressType = {
value?: number; value?: number;
color?: string; color?: string;
}; };
export type AccordionComponentType = {
children?: ReactElement;
open?: string[];
trigger?: string;
};
export type Side = "top" | "right" | "bottom" | "left";
export type ShadTooltipProps = {
delayDuration?: number;
side?: Side;
content: ReactNode;
children: ReactNode;
};

View file

@ -11,7 +11,11 @@ export type TabsContextType = {
addFlow: (flowData?: FlowType, newProject?: boolean) => Promise<String>; addFlow: (flowData?: FlowType, newProject?: boolean) => Promise<String>;
updateFlow: (newFlow: FlowType) => void; updateFlow: (newFlow: FlowType) => void;
incrementNodeId: () => string; incrementNodeId: () => string;
downloadFlow: (flow: FlowType) => void; downloadFlow: (
flow: FlowType,
flowName: string,
flowDescription?: string
) => void;
downloadFlows: () => void; downloadFlows: () => void;
uploadFlows: () => void; uploadFlows: () => void;
uploadFlow: (newFlow?: boolean) => void; uploadFlow: (newFlow?: boolean) => void;
@ -28,6 +32,8 @@ export type TabsContextType = {
) => void; ) => void;
lastCopiedSelection: { nodes: any; edges: any }; lastCopiedSelection: { nodes: any; edges: any };
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void; setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
setTweak: (tweak: any) => void;
getTweak: any;
}; };
export type TabsState = { export type TabsState = {

View file

@ -1,21 +1,3 @@
import {
RocketLaunchIcon,
LinkIcon,
CpuChipIcon,
LightBulbIcon,
CommandLineIcon,
WrenchScrewdriverIcon,
WrenchIcon,
ComputerDesktopIcon,
GiftIcon,
PaperClipIcon,
QuestionMarkCircleIcon,
FingerPrintIcon,
ScissorsIcon,
CircleStackIcon,
Squares2X2Icon,
Bars3CenterLeftIcon,
} from "@heroicons/react/24/outline";
import { Connection, Edge, Node, ReactFlowInstance } from "reactflow"; import { Connection, Edge, Node, ReactFlowInstance } from "reactflow";
import { FlowType, NodeType } from "./types/flow"; import { FlowType, NodeType } from "./types/flow";
import { APITemplateType } from "./types/api"; import { APITemplateType } from "./types/api";
@ -42,7 +24,7 @@ import { SlackIcon } from "./icons/Slack";
import { PineconeIcon } from "./icons/Pinecone"; import { PineconeIcon } from "./icons/Pinecone";
import clsx, { ClassValue } from "clsx"; import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./constants"; import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./flow_constants";
import { ComponentType, SVGProps } from "react"; import { ComponentType, SVGProps } from "react";
import { import {
Cpu, Cpu,
@ -58,12 +40,14 @@ import {
Paperclip, Paperclip,
Rocket, Rocket,
Scissors, Scissors,
FileSearch,
TerminalSquare, TerminalSquare,
Wand2, Wand2,
Wrench, Wrench,
} from "lucide-react"; } from "lucide-react";
import { SupabaseIcon } from "./icons/supabase"; import { SupabaseIcon } from "./icons/supabase";
import { MongoDBIcon } from "./icons/MongoDB"; import { MongoDBIcon } from "./icons/MongoDB";
import { VertexAIIcon } from "./icons/VertexAI";
export function classNames(...classes: Array<string>) { export function classNames(...classes: Array<string>) {
return classes.filter(Boolean).join(" "); return classes.filter(Boolean).join(" ");
@ -139,6 +123,7 @@ export const nodeColors: { [char: string]: string } = {
toolkits: "#DB2C2C", toolkits: "#DB2C2C",
wrappers: "#E6277A", wrappers: "#E6277A",
utilities: "#31A3CC", utilities: "#31A3CC",
retrievers: "#e6b25a",
unknown: "#9CA3AF", unknown: "#9CA3AF",
}; };
@ -157,72 +142,11 @@ export const nodeNames: { [char: string]: string } = {
toolkits: "Toolkits", toolkits: "Toolkits",
wrappers: "Wrappers", wrappers: "Wrappers",
textsplitters: "Text Splitters", textsplitters: "Text Splitters",
retrievers: "Retrievers",
utilities: "Utilities", utilities: "Utilities",
unknown: "Unknown", unknown: "Unknown",
}; };
export const nodeIcons: {
[char: string]: React.ForwardRefExoticComponent<
React.SVGProps<SVGSVGElement>
>;
} = {
Chroma: ChromaIcon,
AirbyteJSONLoader: AirbyteIcon,
// SerpAPIWrapper: SerperIcon,
// AZLyricsLoader: AzIcon,
Anthropic: AnthropicIcon,
ChatAnthropic: AnthropicIcon,
BingSearchAPIWrapper: BingIcon,
BingSearchRun: BingIcon,
Cohere: CohereIcon,
CohereEmbeddings: CohereIcon,
EverNoteLoader: EvernoteIcon,
FacebookChatLoader: FBIcon,
GitbookLoader: GitBookIcon,
GoogleSearchAPIWrapper: GoogleIcon,
GoogleSearchResults: GoogleIcon,
GoogleSearchRun: GoogleIcon,
HNLoader: HackerNewsIcon,
HuggingFaceHub: HugginFaceIcon,
HuggingFaceEmbeddings: HugginFaceIcon,
IFixitLoader: IFixIcon,
Meta: MetaIcon,
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
Searx: SearxIcon,
SlackDirectoryLoader: SlackIcon,
// Weaviate: WeaviateIcon, // does not work
// WikipediaAPIWrapper: WikipediaIcon,
// WolframAlphaQueryRun: WolframIcon,
// WolframAlphaAPIWrapper: WolframIcon,
// UnstructuredWordDocumentLoader: WordIcon, // word and powerpoint have differente styles
agents: RocketLaunchIcon,
chains: LinkIcon,
memories: CpuChipIcon,
llms: LightBulbIcon,
prompts: CommandLineIcon,
tools: WrenchIcon,
advanced: ComputerDesktopIcon,
chat: Bars3CenterLeftIcon,
embeddings: FingerPrintIcon,
documentloaders: PaperClipIcon,
vectorstores: CircleStackIcon,
toolkits: WrenchScrewdriverIcon,
textsplitters: ScissorsIcon,
wrappers: GiftIcon,
utilities: Squares2X2Icon,
unknown: QuestionMarkCircleIcon,
};
export const nodeIconsLucide: { export const nodeIconsLucide: {
[char: string]: React.ForwardRefExoticComponent< [char: string]: React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>> ComponentType<SVGProps<SVGSVGElement>>
@ -318,6 +242,12 @@ export const nodeIconsLucide: {
SupabaseVectorStore: SupabaseIcon as React.ForwardRefExoticComponent< SupabaseVectorStore: SupabaseIcon as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>> ComponentType<SVGProps<SVGSVGElement>>
>, >,
VertexAI: VertexAIIcon as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>>
>,
ChatVertexAI: VertexAIIcon as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>>
>,
agents: Rocket as React.ForwardRefExoticComponent< agents: Rocket as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>> ComponentType<SVGProps<SVGSVGElement>>
>, >,
@ -363,6 +293,9 @@ export const nodeIconsLucide: {
utilities: Wand2 as React.ForwardRefExoticComponent< utilities: Wand2 as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>> ComponentType<SVGProps<SVGSVGElement>>
>, >,
retrievers: FileSearch as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>>
>,
unknown: HelpCircle as React.ForwardRefExoticComponent< unknown: HelpCircle as React.ForwardRefExoticComponent<
ComponentType<SVGProps<SVGSVGElement>> ComponentType<SVGProps<SVGSVGElement>>
>, >,
@ -995,5 +928,5 @@ export function getRandomKeyByssmm(): string {
const now = new Date(); const now = new Date();
const seconds = String(now.getSeconds()).padStart(2, "0"); const seconds = String(now.getSeconds()).padStart(2, "0");
const milliseconds = String(now.getMilliseconds()).padStart(3, "0"); const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
return seconds + milliseconds; return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001));
} }

View file

@ -12,6 +12,12 @@ module.exports = {
"./index.html", "./index.html",
"./src/**/*.{js,ts,tsx,jsx}", "./src/**/*.{js,ts,tsx,jsx}",
], ],
safelist: [
"bg-status-blue",
"bg-status-green",
"bg-status-red",
"bg-status-yellow",
],
important: true, important: true,
theme: { theme: {
container: { container: {
@ -22,6 +28,20 @@ module.exports = {
}, },
}, },
extend: { extend: {
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
colors: { colors: {
"almost-dark-gray": "var(--almost-dark-gray)", "almost-dark-gray": "var(--almost-dark-gray)",
"almost-light-blue": "var(--almost-light-blue)", "almost-light-blue": "var(--almost-light-blue)",

File diff suppressed because one or more lines are too long

View file

@ -26,6 +26,7 @@ def test_zero_shot_agent(client: TestClient):
"type": "LLMChain", "type": "LLMChain",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["allowed_tools"] == { assert template["allowed_tools"] == {
"required": False, "required": False,
@ -37,6 +38,7 @@ def test_zero_shot_agent(client: TestClient):
"type": "Tool", "type": "Tool",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
@ -60,6 +62,7 @@ def test_json_agent(client: TestClient):
"type": "BaseToolkit", "type": "BaseToolkit",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -72,6 +75,7 @@ def test_json_agent(client: TestClient):
"list": False, "list": False,
"advanced": False, "advanced": False,
"display_name": "LLM", "display_name": "LLM",
"info": "",
} }
@ -99,6 +103,7 @@ def test_csv_agent(client: TestClient):
"list": False, "list": False,
"file_path": None, "file_path": None,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -111,6 +116,7 @@ def test_csv_agent(client: TestClient):
"list": False, "list": False,
"advanced": False, "advanced": False,
"display_name": "LLM", "display_name": "LLM",
"info": "",
} }
@ -143,6 +149,7 @@ def test_initialize_agent(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["memory"] == { assert template["memory"] == {
"required": False, "required": False,
@ -154,6 +161,7 @@ def test_initialize_agent(client: TestClient):
"type": "BaseChatMemory", "type": "BaseChatMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["tools"] == { assert template["tools"] == {
"required": False, "required": False,
@ -165,6 +173,7 @@ def test_initialize_agent(client: TestClient):
"type": "Tool", "type": "Tool",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -177,4 +186,5 @@ def test_initialize_agent(client: TestClient):
"list": False, "list": False,
"advanced": False, "advanced": False,
"display_name": "LLM", "display_name": "LLM",
"info": "",
} }

View file

@ -23,7 +23,6 @@ def test_conversation_chain(client: TestClient):
"ConversationChain", "ConversationChain",
"LLMChain", "LLMChain",
"Chain", "Chain",
"Serializable",
"function", "function",
} }
@ -38,6 +37,7 @@ def test_conversation_chain(client: TestClient):
"type": "BaseMemory", "type": "BaseMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -49,6 +49,7 @@ def test_conversation_chain(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -60,6 +61,7 @@ def test_conversation_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["input_key"] == { assert template["input_key"] == {
"required": True, "required": True,
@ -72,6 +74,7 @@ def test_conversation_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["output_key"] == { assert template["output_key"] == {
"required": True, "required": True,
@ -84,6 +87,7 @@ def test_conversation_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["_type"] == "ConversationChain" assert template["_type"] == "ConversationChain"
@ -103,7 +107,6 @@ def test_llm_chain(client: TestClient):
# Test the base classes, template, memory, verbose, llm, input_key, output_key, and _type objects # Test the base classes, template, memory, verbose, llm, input_key, output_key, and _type objects
assert set(chain["base_classes"]) == { assert set(chain["base_classes"]) == {
"Serializable",
"function", "function",
"LLMChain", "LLMChain",
"Chain", "Chain",
@ -120,6 +123,7 @@ def test_llm_chain(client: TestClient):
"type": "BaseMemory", "type": "BaseMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -132,6 +136,7 @@ def test_llm_chain(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -143,6 +148,7 @@ def test_llm_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["output_key"] == { assert template["output_key"] == {
"required": True, "required": True,
@ -155,6 +161,7 @@ def test_llm_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
@ -167,7 +174,6 @@ def test_llm_checker_chain(client: TestClient):
# Test the base classes, template, memory, verbose, llm, input_key, output_key, and _type objects # Test the base classes, template, memory, verbose, llm, input_key, output_key, and _type objects
assert set(chain["base_classes"]) == { assert set(chain["base_classes"]) == {
"Serializable",
"function", "function",
"LLMCheckerChain", "LLMCheckerChain",
"Chain", "Chain",
@ -184,6 +190,7 @@ def test_llm_checker_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["_type"] == "LLMCheckerChain" assert template["_type"] == "LLMCheckerChain"
@ -201,7 +208,6 @@ def test_llm_math_chain(client: TestClient):
# Test the base classes, template, memory, verbose, llm, input_key, output_key, and _type objects # Test the base classes, template, memory, verbose, llm, input_key, output_key, and _type objects
assert set(chain["base_classes"]) == { assert set(chain["base_classes"]) == {
"function", "function",
"Serializable",
"LLMMathChain", "LLMMathChain",
"Chain", "Chain",
} }
@ -217,6 +223,7 @@ def test_llm_math_chain(client: TestClient):
"type": "BaseMemory", "type": "BaseMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -229,6 +236,7 @@ def test_llm_math_chain(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -240,6 +248,7 @@ def test_llm_math_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["input_key"] == { assert template["input_key"] == {
"required": True, "required": True,
@ -252,6 +261,7 @@ def test_llm_math_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["output_key"] == { assert template["output_key"] == {
"required": True, "required": True,
@ -264,6 +274,7 @@ def test_llm_math_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["_type"] == "LLMMathChain" assert template["_type"] == "LLMMathChain"
@ -304,6 +315,7 @@ def test_series_character_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["character"] == { assert template["character"] == {
"required": True, "required": True,
@ -315,6 +327,7 @@ def test_series_character_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["series"] == { assert template["series"] == {
"required": True, "required": True,
@ -326,6 +339,7 @@ def test_series_character_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["_type"] == "SeriesCharacterChain" assert template["_type"] == "SeriesCharacterChain"
@ -367,6 +381,7 @@ def test_mid_journey_prompt_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
# Test the description object # Test the description object
assert ( assert (
@ -406,6 +421,7 @@ def test_time_travel_guide_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["memory"] == { assert template["memory"] == {
"required": False, "required": False,
@ -417,6 +433,7 @@ def test_time_travel_guide_chain(client: TestClient):
"type": "BaseChatMemory", "type": "BaseChatMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert chain["description"] == "Time travel guide chain." assert chain["description"] == "Time travel guide chain."

View file

@ -116,53 +116,53 @@ def test_get_node_neighbors_basic(basic_graph):
) )
def test_get_node_neighbors_complex(complex_graph): # def test_get_node_neighbors_complex(complex_graph):
"""Test getting node neighbors""" # """Test getting node neighbors"""
assert isinstance(complex_graph, Graph) # assert isinstance(complex_graph, Graph)
# Get root node # # Get root node
root = get_root_node(complex_graph) # root = get_root_node(complex_graph)
assert root is not None # assert root is not None
neighbors = complex_graph.get_nodes_with_target(root) # neighbors = complex_graph.get_nodes_with_target(root)
assert neighbors is not None # assert neighbors is not None
# Neighbors should be a list of nodes # # Neighbors should be a list of nodes
assert isinstance(neighbors, list) # assert isinstance(neighbors, list)
# Root Node is an Agent, it requires an LLMChain and tools # # Root Node is an Agent, it requires an LLMChain and tools
# We need to check if there is a Chain in the one of the neighbors' # # We need to check if there is a Chain in the one of the neighbors'
assert any("Chain" in neighbor.data["type"] for neighbor in neighbors) # assert any("Chain" in neighbor.data["type"] for neighbor in neighbors)
# assert Tool is in the neighbors # # assert Tool is in the neighbors
assert any("Tool" in neighbor.data["type"] for neighbor in neighbors) # assert any("Tool" in neighbor.data["type"] for neighbor in neighbors)
# Now on to the Chain's neighbors # # Now on to the Chain's neighbors
chain = next(neighbor for neighbor in neighbors if "Chain" in neighbor.data["type"]) # chain = next(neighbor for neighbor in neighbors if "Chain" in neighbor.data["type"])
chain_neighbors = complex_graph.get_nodes_with_target(chain) # chain_neighbors = complex_graph.get_nodes_with_target(chain)
assert chain_neighbors is not None # assert chain_neighbors is not None
# Check if there is a LLM in the chain's neighbors # # Check if there is a LLM in the chain's neighbors
assert any("OpenAI" in neighbor.data["type"] for neighbor in chain_neighbors) # assert any("OpenAI" in neighbor.data["type"] for neighbor in chain_neighbors)
# Chain should have a Prompt as a neighbor # # Chain should have a Prompt as a neighbor
assert any("Prompt" in neighbor.data["type"] for neighbor in chain_neighbors) # assert any("Prompt" in neighbor.data["type"] for neighbor in chain_neighbors)
# Now on to the Tool's neighbors # # Now on to the Tool's neighbors
tool = next(neighbor for neighbor in neighbors if "Tool" in neighbor.data["type"]) # tool = next(neighbor for neighbor in neighbors if "Tool" in neighbor.data["type"])
tool_neighbors = complex_graph.get_nodes_with_target(tool) # tool_neighbors = complex_graph.get_nodes_with_target(tool)
assert tool_neighbors is not None # assert tool_neighbors is not None
# Check if there is an Agent in the tool's neighbors # # Check if there is an Agent in the tool's neighbors
assert any("Agent" in neighbor.data["type"] for neighbor in tool_neighbors) # assert any("Agent" in neighbor.data["type"] for neighbor in tool_neighbors)
# This Agent has a Tool that has a PythonFunction as func # # This Agent has a Tool that has a PythonFunction as func
agent = next( # agent = next(
neighbor for neighbor in tool_neighbors if "Agent" in neighbor.data["type"] # neighbor for neighbor in tool_neighbors if "Agent" in neighbor.data["type"]
) # )
agent_neighbors = complex_graph.get_nodes_with_target(agent) # agent_neighbors = complex_graph.get_nodes_with_target(agent)
assert agent_neighbors is not None # assert agent_neighbors is not None
# Check if there is a Tool in the agent's neighbors # # Check if there is a Tool in the agent's neighbors
assert any("Tool" in neighbor.data["type"] for neighbor in agent_neighbors) # assert any("Tool" in neighbor.data["type"] for neighbor in agent_neighbors)
# This Tool has a PythonFunction as func # # This Tool has a PythonFunction as func
tool = next( # tool = next(
neighbor for neighbor in agent_neighbors if "Tool" in neighbor.data["type"] # neighbor for neighbor in agent_neighbors if "Tool" in neighbor.data["type"]
) # )
tool_neighbors = complex_graph.get_nodes_with_target(tool) # tool_neighbors = complex_graph.get_nodes_with_target(tool)
assert tool_neighbors is not None # assert tool_neighbors is not None
# Check if there is a PythonFunction in the tool's neighbors # # Check if there is a PythonFunction in the tool's neighbors
assert any( # assert any(
"PythonFunctionTool" in neighbor.data["type"] for neighbor in tool_neighbors # "PythonFunctionTool" in neighbor.data["type"] for neighbor in tool_neighbors
) # )
def test_get_node(basic_graph): def test_get_node(basic_graph):

View file

@ -121,6 +121,7 @@ def test_openai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -132,6 +133,7 @@ def test_openai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["client"] == { assert template["client"] == {
"required": False, "required": False,
@ -143,6 +145,7 @@ def test_openai(client: TestClient):
"type": "Any", "type": "Any",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_name"] == { assert template["model_name"] == {
"required": False, "required": False,
@ -162,6 +165,7 @@ def test_openai(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
# Add more assertions for other properties here # Add more assertions for other properties here
assert template["temperature"] == { assert template["temperature"] == {
@ -175,6 +179,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_tokens"] == { assert template["max_tokens"] == {
"required": False, "required": False,
@ -187,6 +192,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["top_p"] == { assert template["top_p"] == {
"required": False, "required": False,
@ -199,6 +205,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["frequency_penalty"] == { assert template["frequency_penalty"] == {
"required": False, "required": False,
@ -211,6 +218,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["presence_penalty"] == { assert template["presence_penalty"] == {
"required": False, "required": False,
@ -223,6 +231,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["n"] == { assert template["n"] == {
"required": False, "required": False,
@ -235,6 +244,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["best_of"] == { assert template["best_of"] == {
"required": False, "required": False,
@ -247,6 +257,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_kwargs"] == { assert template["model_kwargs"] == {
"required": False, "required": False,
@ -258,6 +269,7 @@ def test_openai(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["openai_api_key"] == { assert template["openai_api_key"] == {
"required": False, "required": False,
@ -271,6 +283,7 @@ def test_openai(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["batch_size"] == { assert template["batch_size"] == {
"required": False, "required": False,
@ -283,6 +296,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["request_timeout"] == { assert template["request_timeout"] == {
"required": False, "required": False,
@ -294,6 +308,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["logit_bias"] == { assert template["logit_bias"] == {
"required": False, "required": False,
@ -305,6 +320,7 @@ def test_openai(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_retries"] == { assert template["max_retries"] == {
"required": False, "required": False,
@ -317,6 +333,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["streaming"] == { assert template["streaming"] == {
"required": False, "required": False,
@ -329,6 +346,7 @@ def test_openai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
@ -352,6 +370,7 @@ def test_chat_open_ai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["client"] == { assert template["client"] == {
"required": False, "required": False,
@ -363,6 +382,7 @@ def test_chat_open_ai(client: TestClient):
"type": "Any", "type": "Any",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_name"] == { assert template["model_name"] == {
"required": False, "required": False,
@ -385,6 +405,7 @@ def test_chat_open_ai(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["temperature"] == { assert template["temperature"] == {
"required": False, "required": False,
@ -397,6 +418,7 @@ def test_chat_open_ai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_kwargs"] == { assert template["model_kwargs"] == {
"required": False, "required": False,
@ -408,6 +430,7 @@ def test_chat_open_ai(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["openai_api_key"] == { assert template["openai_api_key"] == {
"required": False, "required": False,
@ -421,6 +444,7 @@ def test_chat_open_ai(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["request_timeout"] == { assert template["request_timeout"] == {
"required": False, "required": False,
@ -432,6 +456,7 @@ def test_chat_open_ai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_retries"] == { assert template["max_retries"] == {
"required": False, "required": False,
@ -444,6 +469,7 @@ def test_chat_open_ai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["streaming"] == { assert template["streaming"] == {
"required": False, "required": False,
@ -456,6 +482,7 @@ def test_chat_open_ai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["n"] == { assert template["n"] == {
"required": False, "required": False,
@ -468,6 +495,7 @@ def test_chat_open_ai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_tokens"] == { assert template["max_tokens"] == {
@ -480,6 +508,7 @@ def test_chat_open_ai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["_type"] == "ChatOpenAI" assert template["_type"] == "ChatOpenAI"
assert ( assert (
@ -487,7 +516,7 @@ def test_chat_open_ai(client: TestClient):
== "Wrapper around OpenAI Chat large language models." # noqa E501 == "Wrapper around OpenAI Chat large language models." # noqa E501
) )
assert set(model["base_classes"]) == { assert set(model["base_classes"]) == {
"Serializable", "BaseLLM",
"BaseChatModel", "BaseChatModel",
"ChatOpenAI", "ChatOpenAI",
"BaseLanguageModel", "BaseLanguageModel",

View file

@ -28,6 +28,7 @@ def test_prompt_template(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["output_parser"] == { assert template["output_parser"] == {
"required": False, "required": False,
@ -39,6 +40,7 @@ def test_prompt_template(client: TestClient):
"type": "BaseOutputParser", "type": "BaseOutputParser",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["partial_variables"] == { assert template["partial_variables"] == {
"required": False, "required": False,
@ -50,6 +52,7 @@ def test_prompt_template(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["template"] == { assert template["template"] == {
"required": True, "required": True,
@ -61,6 +64,7 @@ def test_prompt_template(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["template_format"] == { assert template["template_format"] == {
"required": False, "required": False,
@ -73,6 +77,7 @@ def test_prompt_template(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["validate_template"] == { assert template["validate_template"] == {
"required": False, "required": False,
@ -85,85 +90,7 @@ def test_prompt_template(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
} "info": "",
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,
} }
@ -185,6 +112,7 @@ def test_zero_shot_prompt(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["suffix"] == { assert template["suffix"] == {
"required": True, "required": True,
@ -197,6 +125,7 @@ def test_zero_shot_prompt(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["format_instructions"] == { assert template["format_instructions"] == {
"required": True, "required": True,
@ -209,4 +138,5 @@ def test_zero_shot_prompt(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }

View file

@ -7,7 +7,7 @@ import pytest
def test_init_build(client): def test_init_build(client):
response = client.post( response = client.post(
"api/v1/build/init", json={"id": "test", "data": {"key": "value"}} "api/v1/build/init/test", json={"id": "test", "data": {"key": "value"}}
) )
assert response.status_code == 201 assert response.status_code == 201
assert response.json() == {"flowId": "test"} assert response.json() == {"flowId": "test"}
@ -43,7 +43,5 @@ def test_websocket_endpoint_after_build(client, basic_graph_data):
# and how your chat_manager and other classes behave. The following is just an example structure. # and how your chat_manager and other classes behave. The following is just an example structure.
with pytest.raises(WebSocketDisconnect): with pytest.raises(WebSocketDisconnect):
with client.websocket_connect("api/v1/chat/websocket_test") as websocket: with client.websocket_connect("api/v1/chat/websocket_test") as websocket:
websocket.send_json({"type": "test"}) websocket.send_json({"input": "test"})
# Perform assertions here, based on what you expect the websocket to return websocket.receive_json()
# data = websocket.receive_json()
# assert ...