Merge branch 'release' into dev
This commit is contained in:
commit
b61a0a624e
40 changed files with 771 additions and 434 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import sys
|
||||
import time
|
||||
from fastapi import FastAPI
|
||||
import httpx
|
||||
from multiprocess import Process, cpu_count # type: ignore
|
||||
import platform
|
||||
|
|
@ -11,9 +10,7 @@ from rich.panel import Panel
|
|||
from rich import box
|
||||
from rich import print as rprint
|
||||
import typer
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from langflow.main import create_app
|
||||
from langflow.main import setup_app
|
||||
from langflow.settings import settings
|
||||
from langflow.utils.logger import configure, logger
|
||||
import webbrowser
|
||||
|
|
@ -144,15 +141,9 @@ def serve(
|
|||
remove_api_keys=remove_api_keys,
|
||||
cache=cache,
|
||||
)
|
||||
# get the directory of the current file
|
||||
if not path:
|
||||
frontend_path = Path(__file__).parent
|
||||
static_files_dir = frontend_path / "frontend"
|
||||
else:
|
||||
static_files_dir = Path(path)
|
||||
|
||||
app = create_app()
|
||||
setup_static_files(app, static_files_dir)
|
||||
# create path object if path is provided
|
||||
static_files_dir: Optional[Path] = Path(path) if path else None
|
||||
app = setup_app(static_files_dir=static_files_dir)
|
||||
# check if port is being used
|
||||
if is_port_in_use(port, host):
|
||||
port = get_free_port(port)
|
||||
|
|
@ -200,29 +191,6 @@ def run_on_windows(host, port, log_level, options, app):
|
|||
run_langflow(host, port, log_level, options, app)
|
||||
|
||||
|
||||
def setup_static_files(app: FastAPI, static_files_dir: Path):
|
||||
"""
|
||||
Setup the static files directory.
|
||||
|
||||
Args:
|
||||
app (FastAPI): FastAPI app.
|
||||
path (str): Path to the static files directory.
|
||||
"""
|
||||
app.mount(
|
||||
"/",
|
||||
StaticFiles(directory=static_files_dir, html=True),
|
||||
name="static",
|
||||
)
|
||||
|
||||
@app.exception_handler(404)
|
||||
async def custom_404_handler(request, __):
|
||||
path = static_files_dir / "index.html"
|
||||
|
||||
if not path.exists():
|
||||
raise RuntimeError(f"File at path {path} does not exist.")
|
||||
return FileResponse(path)
|
||||
|
||||
|
||||
def is_port_in_use(port, host="localhost"):
|
||||
"""
|
||||
Check if a port is in use.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
from fastapi import (
|
||||
APIRouter,
|
||||
HTTPException,
|
||||
WebSocket,
|
||||
WebSocketException,
|
||||
status,
|
||||
)
|
||||
from fastapi import APIRouter, HTTPException, WebSocket, WebSocketException, status
|
||||
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.graph.graph.base import Graph
|
||||
|
|
@ -32,15 +26,29 @@ async def chat(client_id: str, websocket: WebSocket):
|
|||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
||||
|
||||
|
||||
@router.post("/build/init", response_model=InitResponse, status_code=201)
|
||||
async def init_build(graph_data: dict):
|
||||
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
|
||||
async def init_build(graph_data: dict, flow_id: str):
|
||||
"""Initialize the build by storing graph data and returning a unique session ID."""
|
||||
|
||||
try:
|
||||
flow_id = graph_data.get("id")
|
||||
if flow_id is None:
|
||||
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.IN_PROGRESS,
|
||||
}
|
||||
|
||||
return InitResponse(flowId=flow_id)
|
||||
except Exception as exc:
|
||||
|
|
@ -52,8 +60,9 @@ async def init_build(graph_data: dict):
|
|||
async def build_status(flow_id: str):
|
||||
"""Check the flow_id is in the flow_data_store."""
|
||||
try:
|
||||
built = flow_id in flow_data_store and not isinstance(
|
||||
flow_data_store[flow_id], dict
|
||||
built = (
|
||||
flow_id in flow_data_store
|
||||
and flow_data_store[flow_id]["status"] == BuildStatus.SUCCESS
|
||||
)
|
||||
|
||||
return BuiltResponse(
|
||||
|
|
@ -77,6 +86,11 @@ async def stream_build(flow_id: str):
|
|||
yield str(StreamData(event="error", data={"error": error_message}))
|
||||
return
|
||||
|
||||
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("data")
|
||||
|
||||
if not graph_data:
|
||||
|
|
@ -110,6 +124,7 @@ async def stream_build(flow_id: str):
|
|||
except Exception as exc:
|
||||
params = str(exc)
|
||||
valid = False
|
||||
flow_data_store[flow_id]["status"] = BuildStatus.FAILURE
|
||||
|
||||
response = {
|
||||
"valid": valid,
|
||||
|
|
@ -121,8 +136,10 @@ async def stream_build(flow_id: str):
|
|||
yield str(StreamData(event="message", data=response))
|
||||
|
||||
chat_manager.set_cache(flow_id, graph.build())
|
||||
flow_data_store[flow_id]["status"] = BuildStatus.SUCCESS
|
||||
except Exception as 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)}))
|
||||
finally:
|
||||
yield str(StreamData(event="message", data=final_response))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from langflow.database.models.flow import FlowCreate, FlowRead
|
||||
|
|
@ -5,6 +6,14 @@ from pydantic import BaseModel, Field, validator
|
|||
import json
|
||||
|
||||
|
||||
class BuildStatus(Enum):
|
||||
"""Status of the build."""
|
||||
|
||||
SUCCESS = "success"
|
||||
FAILURE = "failure"
|
||||
IN_PROGRESS = "in_progress"
|
||||
|
||||
|
||||
class GraphData(BaseModel):
|
||||
"""Data inside the exported flow."""
|
||||
|
||||
|
|
|
|||
2
src/backend/langflow/chat/config.py
Normal file
2
src/backend/langflow/chat/config.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
class ChatConfig:
|
||||
streaming: bool = True
|
||||
|
|
@ -1,142 +1,250 @@
|
|||
---
|
||||
agents:
|
||||
- ZeroShotAgent
|
||||
- JsonAgent
|
||||
- CSVAgent
|
||||
- AgentInitializer
|
||||
- VectorStoreAgent
|
||||
- VectorStoreRouterAgent
|
||||
- SQLAgent
|
||||
ZeroShotAgent:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
|
||||
JsonAgent:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/toolkits/openapi"
|
||||
CSVAgent:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/toolkits/csv"
|
||||
AgentInitializer:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/agent_types/"
|
||||
VectorStoreAgent:
|
||||
documentation: ""
|
||||
VectorStoreRouterAgent:
|
||||
documentation: ""
|
||||
SQLAgent:
|
||||
documentation: ""
|
||||
chains:
|
||||
- LLMChain
|
||||
- LLMMathChain
|
||||
- LLMCheckerChain
|
||||
- ConversationChain
|
||||
- SeriesCharacterChain
|
||||
- MidJourneyPromptChain
|
||||
- TimeTravelGuideChain
|
||||
- SQLDatabaseChain
|
||||
- RetrievalQA
|
||||
- RetrievalQAWithSourcesChain
|
||||
- ConversationalRetrievalChain
|
||||
- CombineDocsChain
|
||||
LLMChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/foundational/llm_chain"
|
||||
LLMMathChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_math"
|
||||
LLMCheckerChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_checker"
|
||||
ConversationChain:
|
||||
documentation: ""
|
||||
SeriesCharacterChain:
|
||||
documentation: ""
|
||||
MidJourneyPromptChain:
|
||||
documentation: ""
|
||||
TimeTravelGuideChain:
|
||||
documentation: ""
|
||||
SQLDatabaseChain:
|
||||
documentation: ""
|
||||
RetrievalQA:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/popular/vector_db_qa"
|
||||
RetrievalQAWithSourcesChain:
|
||||
documentation: ""
|
||||
ConversationalRetrievalChain:
|
||||
documentation: "https://python.langchain.com/docs/modules/chains/popular/chat_vector_db"
|
||||
CombineDocsChain:
|
||||
documentation: ""
|
||||
documentloaders:
|
||||
- AirbyteJSONLoader
|
||||
- CoNLLULoader
|
||||
- CSVLoader
|
||||
- UnstructuredEmailLoader
|
||||
- EverNoteLoader
|
||||
- FacebookChatLoader
|
||||
- GutenbergLoader
|
||||
- BSHTMLLoader
|
||||
- UnstructuredHTMLLoader
|
||||
# - UnstructuredImageLoader # Issue with Python 3.11 (https://github.com/Unstructured-IO/unstructured-inference/issues/83)
|
||||
- UnstructuredMarkdownLoader
|
||||
- PyPDFLoader
|
||||
- UnstructuredPowerPointLoader
|
||||
- SRTLoader
|
||||
- TelegramChatLoader
|
||||
- TextLoader
|
||||
- UnstructuredWordDocumentLoader
|
||||
- WebBaseLoader
|
||||
- AZLyricsLoader
|
||||
- CollegeConfidentialLoader
|
||||
- HNLoader
|
||||
- IFixitLoader
|
||||
- IMSDbLoader
|
||||
- GitbookLoader
|
||||
- ReadTheDocsLoader
|
||||
- SlackDirectoryLoader
|
||||
- NotionDirectoryLoader
|
||||
- DirectoryLoader
|
||||
- GitLoader
|
||||
AirbyteJSONLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/airbyte_json"
|
||||
CoNLLULoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/conll-u"
|
||||
CSVLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/csv"
|
||||
UnstructuredEmailLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/email"
|
||||
EverNoteLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/evernote"
|
||||
FacebookChatLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/facebook_chat"
|
||||
GutenbergLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gutenberg"
|
||||
BSHTMLLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
|
||||
UnstructuredHTMLLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
|
||||
UnstructuredMarkdownLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/markdown"
|
||||
PyPDFLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/pdf"
|
||||
UnstructuredPowerPointLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_powerpoint"
|
||||
SRTLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/subtitle"
|
||||
TelegramChatLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/telegram"
|
||||
TextLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/"
|
||||
UnstructuredWordDocumentLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_word"
|
||||
WebBaseLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/web_base"
|
||||
AZLyricsLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/azlyrics"
|
||||
CollegeConfidentialLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/college_confidential"
|
||||
HNLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/hacker_news"
|
||||
IFixitLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/ifixit"
|
||||
IMSDbLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/imsdb"
|
||||
GitbookLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gitbook"
|
||||
ReadTheDocsLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/readthedocs_documentation"
|
||||
SlackDirectoryLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/slack"
|
||||
NotionDirectoryLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/notion"
|
||||
DirectoryLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/file_directory"
|
||||
GitLoader:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/git"
|
||||
embeddings:
|
||||
- OpenAIEmbeddings
|
||||
- HuggingFaceEmbeddings
|
||||
- CohereEmbeddings
|
||||
OpenAIEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai"
|
||||
HuggingFaceEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers"
|
||||
CohereEmbeddings:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/cohere"
|
||||
llms:
|
||||
- OpenAI
|
||||
# - AzureOpenAI
|
||||
# - AzureChatOpenAI
|
||||
- ChatOpenAI
|
||||
- LlamaCpp
|
||||
- CTransformers
|
||||
- Cohere
|
||||
- Anthropic
|
||||
- ChatAnthropic
|
||||
- HuggingFaceHub
|
||||
OpenAI:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/openai"
|
||||
ChatOpenAI:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai"
|
||||
LlamaCpp:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
|
||||
CTransformers:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
|
||||
Cohere:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
|
||||
Anthropic:
|
||||
documentation: ""
|
||||
ChatAnthropic:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
|
||||
HuggingFaceHub:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/huggingface_hub"
|
||||
memories:
|
||||
- ConversationBufferMemory
|
||||
- ConversationSummaryMemory
|
||||
- ConversationKGMemory
|
||||
- PostgresChatMessageHistory
|
||||
PostgresChatMessageHistory:
|
||||
documentation: "https://python.langchain.com/docs/modules/memory/how_to/agent_with_memory_in_db"
|
||||
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:
|
||||
- PromptTemplate
|
||||
- FewShotPromptTemplate
|
||||
- ZeroShotPrompt
|
||||
PromptTemplate:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
|
||||
ZeroShotPrompt:
|
||||
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
|
||||
textsplitters:
|
||||
- CharacterTextSplitter
|
||||
- RecursiveCharacterTextSplitter
|
||||
# - LatexTextSplitter
|
||||
# - PythonCodeTextSplitter
|
||||
CharacterTextSplitter:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
|
||||
RecursiveCharacterTextSplitter:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter"
|
||||
toolkits:
|
||||
- OpenAPIToolkit
|
||||
- JsonToolkit
|
||||
- VectorStoreInfo
|
||||
- VectorStoreRouterToolkit
|
||||
- VectorStoreToolkit
|
||||
OpenAPIToolkit:
|
||||
documentation: ""
|
||||
JsonToolkit:
|
||||
documentation: ""
|
||||
VectorStoreInfo:
|
||||
documentation: ""
|
||||
VectorStoreRouterToolkit:
|
||||
documentation: ""
|
||||
VectorStoreToolkit:
|
||||
documentation: ""
|
||||
tools:
|
||||
- Search
|
||||
- PAL-MATH
|
||||
- Calculator
|
||||
- Serper Search
|
||||
- Tool
|
||||
- PythonFunctionTool
|
||||
- PythonFunction
|
||||
- JsonSpec
|
||||
- News API
|
||||
- TMDB API
|
||||
- Podcast API
|
||||
- QuerySQLDataBaseTool
|
||||
- InfoSQLDatabaseTool
|
||||
- ListSQLDatabaseTool
|
||||
# - QueryCheckerTool
|
||||
- BingSearchRun
|
||||
- GoogleSearchRun
|
||||
- GoogleSearchResults
|
||||
- GoogleSerperRun
|
||||
- JsonListKeysTool
|
||||
- JsonGetValueTool
|
||||
- PythonREPLTool
|
||||
- PythonAstREPLTool
|
||||
- RequestsGetTool
|
||||
- RequestsPostTool
|
||||
- RequestsPatchTool
|
||||
- RequestsPutTool
|
||||
- RequestsDeleteTool
|
||||
- WikipediaQueryRun
|
||||
- WolframAlphaQueryRun
|
||||
Search:
|
||||
documentation: ""
|
||||
PAL-MATH:
|
||||
documentation: ""
|
||||
Calculator:
|
||||
documentation: ""
|
||||
Serper Search:
|
||||
documentation: ""
|
||||
Tool:
|
||||
documentation: ""
|
||||
PythonFunctionTool:
|
||||
documentation: ""
|
||||
PythonFunction:
|
||||
documentation: ""
|
||||
JsonSpec:
|
||||
documentation: ""
|
||||
News API:
|
||||
documentation: ""
|
||||
TMDB API:
|
||||
documentation: ""
|
||||
Podcast API:
|
||||
documentation: ""
|
||||
QuerySQLDataBaseTool:
|
||||
documentation: ""
|
||||
InfoSQLDatabaseTool:
|
||||
documentation: ""
|
||||
ListSQLDatabaseTool:
|
||||
documentation: ""
|
||||
BingSearchRun:
|
||||
documentation: ""
|
||||
GoogleSearchRun:
|
||||
documentation: ""
|
||||
GoogleSearchResults:
|
||||
documentation: ""
|
||||
GoogleSerperRun:
|
||||
documentation: ""
|
||||
JsonListKeysTool:
|
||||
documentation: ""
|
||||
JsonGetValueTool:
|
||||
documentation: ""
|
||||
PythonREPLTool:
|
||||
documentation: ""
|
||||
PythonAstREPLTool:
|
||||
documentation: ""
|
||||
RequestsGetTool:
|
||||
documentation: ""
|
||||
RequestsPostTool:
|
||||
documentation: ""
|
||||
RequestsPatchTool:
|
||||
documentation: ""
|
||||
RequestsPutTool:
|
||||
documentation: ""
|
||||
RequestsDeleteTool:
|
||||
documentation: ""
|
||||
WikipediaQueryRun:
|
||||
documentation: ""
|
||||
WolframAlphaQueryRun:
|
||||
documentation: ""
|
||||
utilities:
|
||||
- BingSearchAPIWrapper
|
||||
- GoogleSearchAPIWrapper
|
||||
- GoogleSerperAPIWrapper
|
||||
- SearxResults
|
||||
- SearxSearchWrapper
|
||||
- SerpAPIWrapper
|
||||
- WikipediaAPIWrapper
|
||||
- WolframAlphaAPIWrapper
|
||||
# - ZapierNLAWrapper
|
||||
- SQLDatabase
|
||||
BingSearchAPIWrapper:
|
||||
documentation: ""
|
||||
GoogleSearchAPIWrapper:
|
||||
documentation: ""
|
||||
GoogleSerperAPIWrapper:
|
||||
documentation: ""
|
||||
SearxResults:
|
||||
documentation: ""
|
||||
SearxSearchWrapper:
|
||||
documentation: ""
|
||||
SerpAPIWrapper:
|
||||
documentation: ""
|
||||
WikipediaAPIWrapper:
|
||||
documentation: ""
|
||||
WolframAlphaAPIWrapper:
|
||||
documentation: ""
|
||||
vectorstores:
|
||||
- Chroma
|
||||
- Qdrant
|
||||
- Weaviate
|
||||
- FAISS
|
||||
- Pinecone
|
||||
- SupabaseVectorStore
|
||||
- MongoDBAtlasVectorSearch
|
||||
Chroma:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
|
||||
Qdrant:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
|
||||
Weaviate:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/weaviate"
|
||||
FAISS:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
|
||||
Pinecone:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone"
|
||||
SupabaseVectorStore:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/supabase"
|
||||
MongoDBAtlasVectorSearch:
|
||||
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/mongodb_atlas_vector_search"
|
||||
wrappers:
|
||||
- RequestsWrapper
|
||||
# - ChatPromptTemplate
|
||||
# - SystemMessagePromptTemplate
|
||||
# - HumanMessagePromptTemplate
|
||||
RequestsWrapper:
|
||||
documentation: ""
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from langflow.template.field.base import TemplateField
|
|||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.template.base import Template
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.settings import settings
|
||||
|
||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||
|
||||
|
|
@ -15,12 +16,29 @@ from langflow.utils.logger import logger
|
|||
class LangChainTypeCreator(BaseModel, ABC):
|
||||
type_name: str
|
||||
type_dict: Optional[Dict] = None
|
||||
name_docs_dict: Optional[Dict[str, str]] = None
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[FrontendNode]:
|
||||
"""The class type of the FrontendNode created in frontend_node."""
|
||||
return FrontendNode
|
||||
|
||||
@property
|
||||
def docs_map(self) -> Dict[str, str]:
|
||||
"""A dict with the name of the component as key and the documentation link as value."""
|
||||
if self.name_docs_dict is None:
|
||||
try:
|
||||
type_settings = getattr(settings, self.type_name)
|
||||
self.name_docs_dict = {
|
||||
name: value_dict["documentation"]
|
||||
for name, value_dict in type_settings.items()
|
||||
}
|
||||
except AttributeError as exc:
|
||||
logger.error(exc)
|
||||
|
||||
self.name_docs_dict = {}
|
||||
return self.name_docs_dict
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
|
|
@ -83,7 +101,7 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
|
||||
signature.add_extra_fields()
|
||||
signature.add_extra_base_classes()
|
||||
|
||||
signature.set_documentation(self.docs_map.get(name, ""))
|
||||
return signature
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
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 agent as agent_module
|
||||
from langchain.agents.agent import AgentExecutor
|
||||
from langchain.agents.agent_toolkits.base import BaseToolkit
|
||||
from langchain.agents.tools import BaseTool
|
||||
|
||||
from langflow.interface.initialize.vector_store import vecstore_initializer
|
||||
|
||||
from pydantic import ValidationError
|
||||
|
|
@ -16,6 +17,10 @@ from langflow.interface.toolkits.base import toolkits_creator
|
|||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.utils import load_file_into_dict
|
||||
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:
|
||||
|
|
@ -43,8 +48,8 @@ def convert_params_to_sets(params):
|
|||
|
||||
def convert_kwargs(params):
|
||||
# if *kwargs are passed as a string, convert to dict
|
||||
# first find any key that has kwargs in it
|
||||
kwargs_keys = [key for key in params.keys() if "kwargs" in key]
|
||||
# first find any key that has kwargs or config in it
|
||||
kwargs_keys = [key for key in params.keys() if "kwargs" in key or "config" in key]
|
||||
for key in kwargs_keys:
|
||||
if isinstance(params[key], str):
|
||||
params[key] = json.loads(params[key])
|
||||
|
|
@ -72,11 +77,17 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
|
|||
return instantiate_utility(node_type, class_object, params)
|
||||
elif base_type == "chains":
|
||||
return instantiate_chains(node_type, class_object, params)
|
||||
elif base_type == "llms":
|
||||
return instantiate_llm(node_type, class_object, params)
|
||||
else:
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_chains(node_type, class_object, params):
|
||||
def instantiate_llm(node_type, class_object, params: Dict):
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_chains(node_type, class_object: Type[Chain], params: Dict):
|
||||
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
||||
params["retriever"] = params["retriever"].as_retriever()
|
||||
if node_type in chain_creator.from_method_nodes:
|
||||
|
|
@ -88,11 +99,11 @@ def instantiate_chains(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_agent(class_object, params):
|
||||
def instantiate_agent(class_object: Type[agent_module.Agent], params: Dict):
|
||||
return load_agent_executor(class_object, params)
|
||||
|
||||
|
||||
def instantiate_prompt(node_type, class_object, params):
|
||||
def instantiate_prompt(node_type, class_object: Type[BasePromptTemplate], params: Dict):
|
||||
if node_type == "ZeroShotPrompt":
|
||||
if "tools" not in params:
|
||||
params["tools"] = []
|
||||
|
|
@ -100,7 +111,7 @@ def instantiate_prompt(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_tool(node_type, class_object, params):
|
||||
def instantiate_tool(node_type, class_object: Type[BaseTool], params: Dict):
|
||||
if node_type == "JsonSpec":
|
||||
params["dict_"] = load_file_into_dict(params.pop("path"))
|
||||
return class_object(**params)
|
||||
|
|
@ -118,7 +129,7 @@ def instantiate_tool(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_toolkit(node_type, class_object, params):
|
||||
def instantiate_toolkit(node_type, class_object: Type[BaseToolkit], params: Dict):
|
||||
loaded_toolkit = class_object(**params)
|
||||
# Commenting this out for now to use toolkits as normal tools
|
||||
# if toolkits_creator.has_create_function(node_type):
|
||||
|
|
@ -128,7 +139,7 @@ def instantiate_toolkit(node_type, class_object, params):
|
|||
return loaded_toolkit
|
||||
|
||||
|
||||
def instantiate_embedding(class_object, params):
|
||||
def instantiate_embedding(class_object, params: Dict):
|
||||
params.pop("model", None)
|
||||
params.pop("headers", None)
|
||||
try:
|
||||
|
|
@ -142,7 +153,7 @@ def instantiate_embedding(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", {})
|
||||
if initializer := vecstore_initializer.get(class_object.__name__):
|
||||
vecstore = initializer(class_object, params)
|
||||
|
|
@ -158,7 +169,7 @@ def instantiate_vectorstore(class_object, params):
|
|||
return vecstore
|
||||
|
||||
|
||||
def instantiate_documentloader(class_object, params):
|
||||
def instantiate_documentloader(class_object: Type[BaseLoader], params: Dict):
|
||||
if "file_filter" in params:
|
||||
# file_filter will be a string but we need a function
|
||||
# that will be used to filter the files using file_filter
|
||||
|
|
@ -171,35 +182,55 @@ def instantiate_documentloader(class_object, params):
|
|||
extension.strip() in x for extension in extensions
|
||||
)
|
||||
metadata = params.pop("metadata", None)
|
||||
if metadata and isinstance(metadata, str):
|
||||
try:
|
||||
metadata = json.loads(metadata)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError(
|
||||
"The metadata you provided is not a valid JSON string."
|
||||
) from exc
|
||||
docs = class_object(**params).load()
|
||||
# Now if metadata is an empty dict, we will not add it to the documents
|
||||
if metadata:
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
def instantiate_textsplitter(class_object, params):
|
||||
def instantiate_textsplitter(
|
||||
class_object,
|
||||
params: Dict,
|
||||
):
|
||||
try:
|
||||
documents = params.pop("documents")
|
||||
except KeyError as e:
|
||||
except KeyError as exc:
|
||||
raise ValueError(
|
||||
"The source you provided did not load correctly or was empty."
|
||||
"Try changing the chunk_size of the Text Splitter."
|
||||
) from e
|
||||
text_splitter = class_object(**params)
|
||||
) from exc
|
||||
|
||||
if (
|
||||
"separator_type" in params and params["separator_type"] == "Text"
|
||||
) or "separator_type" not in params:
|
||||
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)
|
||||
|
||||
|
||||
def instantiate_utility(node_type, class_object, params):
|
||||
def instantiate_utility(node_type, class_object, params: Dict):
|
||||
if node_type == "SQLDatabase":
|
||||
return class_object.from_uri(params.pop("uri"))
|
||||
return class_object(**params)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import os
|
|||
from io import BytesIO
|
||||
import re
|
||||
|
||||
|
||||
import yaml
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from PIL.Image import Image
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.chat.config import ChatConfig
|
||||
|
||||
|
||||
def load_file_into_dict(file_path: str) -> dict:
|
||||
|
|
@ -49,9 +51,9 @@ def try_setting_streaming_options(langchain_object, websocket):
|
|||
|
||||
if isinstance(llm, BaseLanguageModel):
|
||||
if hasattr(llm, "streaming") and isinstance(llm.streaming, bool):
|
||||
llm.streaming = True
|
||||
llm.streaming = ChatConfig.streaming
|
||||
elif hasattr(llm, "stream") and isinstance(llm.stream, bool):
|
||||
llm.stream = True
|
||||
llm.stream = ChatConfig.streaming
|
||||
|
||||
return langchain_object
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from langflow.api import router
|
||||
from langflow.database.base import create_db_and_tables
|
||||
|
|
@ -33,6 +37,42 @@ def create_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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
import os
|
||||
from typing import List
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseSettings, root_validator
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
chains: List[str] = []
|
||||
agents: List[str] = []
|
||||
prompts: List[str] = []
|
||||
llms: List[str] = []
|
||||
tools: List[str] = []
|
||||
memories: List[str] = []
|
||||
embeddings: List[str] = []
|
||||
vectorstores: List[str] = []
|
||||
documentloaders: List[str] = []
|
||||
wrappers: List[str] = []
|
||||
toolkits: List[str] = []
|
||||
textsplitters: List[str] = []
|
||||
utilities: List[str] = []
|
||||
chains: dict = {}
|
||||
agents: dict = {}
|
||||
prompts: dict = {}
|
||||
llms: dict = {}
|
||||
tools: dict = {}
|
||||
memories: dict = {}
|
||||
embeddings: dict = {}
|
||||
vectorstores: dict = {}
|
||||
documentloaders: dict = {}
|
||||
wrappers: dict = {}
|
||||
toolkits: dict = {}
|
||||
textsplitters: dict = {}
|
||||
utilities: dict = {}
|
||||
dev: bool = False
|
||||
database_url: str = "sqlite:///./langflow.db"
|
||||
cache: str = "InMemoryCache"
|
||||
|
|
@ -38,16 +37,16 @@ class Settings(BaseSettings):
|
|||
|
||||
def update_from_yaml(self, file_path: str, dev: bool = False):
|
||||
new_settings = load_settings_from_yaml(file_path)
|
||||
self.chains = new_settings.chains or []
|
||||
self.agents = new_settings.agents or []
|
||||
self.prompts = new_settings.prompts or []
|
||||
self.llms = new_settings.llms or []
|
||||
self.tools = new_settings.tools or []
|
||||
self.memories = new_settings.memories or []
|
||||
self.wrappers = new_settings.wrappers or []
|
||||
self.toolkits = new_settings.toolkits or []
|
||||
self.textsplitters = new_settings.textsplitters or []
|
||||
self.utilities = new_settings.utilities or []
|
||||
self.chains = new_settings.chains or {}
|
||||
self.agents = new_settings.agents or {}
|
||||
self.prompts = new_settings.prompts or {}
|
||||
self.llms = new_settings.llms or {}
|
||||
self.tools = new_settings.tools or {}
|
||||
self.memories = new_settings.memories or {}
|
||||
self.wrappers = new_settings.wrappers or {}
|
||||
self.toolkits = new_settings.toolkits or {}
|
||||
self.textsplitters = new_settings.textsplitters or {}
|
||||
self.utilities = new_settings.utilities or {}
|
||||
self.dev = dev
|
||||
|
||||
def update_settings(self, **kwargs):
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class TemplateFieldCreator(BaseModel, ABC):
|
|||
name: str = ""
|
||||
display_name: Optional[str] = None
|
||||
advanced: bool = False
|
||||
info: Optional[str] = ""
|
||||
|
||||
def to_dict(self):
|
||||
result = self.dict()
|
||||
|
|
|
|||
|
|
@ -15,14 +15,21 @@ class FrontendNode(BaseModel):
|
|||
base_classes: List[str]
|
||||
name: str = ""
|
||||
display_name: str = ""
|
||||
documentation: str = ""
|
||||
|
||||
def set_documentation(self, documentation: str) -> None:
|
||||
"""Sets the documentation of the frontend node."""
|
||||
self.documentation = documentation
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Returns a dict representation of the frontend node."""
|
||||
return {
|
||||
self.name: {
|
||||
"template": self.template.to_dict(self.format_field),
|
||||
"description": self.description,
|
||||
"base_classes": self.base_classes,
|
||||
"display_name": self.display_name or self.name,
|
||||
"documentation": self.documentation,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,3 +32,29 @@ You are a good listener and you can talk about anything.
|
|||
HUMAN_PROMPT = "{input}"
|
||||
|
||||
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.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import json
|
||||
from typing import Optional
|
||||
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.frontend_node.constants import CTRANSFORMERS_DEFAULT_CONFIG
|
||||
from langflow.template.frontend_node.constants import OPENAI_API_BASE_INFO
|
||||
|
||||
|
||||
class LLMFrontendNode(FrontendNode):
|
||||
|
|
@ -15,6 +18,9 @@ class LLMFrontendNode(FrontendNode):
|
|||
if "key" not in field.name.lower() and "token" not in field.name.lower():
|
||||
field.password = False
|
||||
|
||||
if field.name == "openai_api_base":
|
||||
field.info = OPENAI_API_BASE_INFO
|
||||
|
||||
@staticmethod
|
||||
def format_azure_field(field: TemplateField):
|
||||
if field.name == "model_name":
|
||||
|
|
@ -31,6 +37,13 @@ class LLMFrontendNode(FrontendNode):
|
|||
field.show = True
|
||||
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
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
display_names_dict = {
|
||||
|
|
@ -38,6 +51,7 @@ class LLMFrontendNode(FrontendNode):
|
|||
}
|
||||
FrontendNode.format_field(field, name)
|
||||
LLMFrontendNode.format_openai_field(field)
|
||||
LLMFrontendNode.format_ctransformers_field(field)
|
||||
if name and "azure" in name.lower():
|
||||
LLMFrontendNode.format_azure_field(field)
|
||||
if name and "llama" in name.lower():
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langchain.text_splitter import Language
|
||||
|
||||
|
||||
class TextSplittersFrontendNode(FrontendNode):
|
||||
|
|
@ -17,6 +18,24 @@ class TextSplittersFrontendNode(FrontendNode):
|
|||
name = "separator"
|
||||
elif self.template.type_name == "RecursiveCharacterTextSplitter":
|
||||
name = "separators"
|
||||
# Add a field for type of separator
|
||||
# which will have Text or any value from the
|
||||
# Language enum
|
||||
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(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ class VectorStoreFrontendNode(FrontendNode):
|
|||
self.template.add_field(field)
|
||||
|
||||
def add_extra_base_classes(self) -> None:
|
||||
self.base_classes.append("BaseRetriever")
|
||||
self.base_classes.extend(("BaseRetriever", "VectorStoreRetriever"))
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue