Merge remote-tracking branch 'origin/dev' into feature/output_dropdown
This commit is contained in:
commit
bd4f4c8724
318 changed files with 14679 additions and 9306 deletions
1
src/backend/.gitignore
vendored
1
src/backend/.gitignore
vendored
|
|
@ -131,3 +131,4 @@ dmypy.json
|
|||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
*.db
|
||||
|
|
@ -2,9 +2,9 @@ import platform
|
|||
import socket
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import warnings
|
||||
|
||||
import click
|
||||
import httpx
|
||||
|
|
@ -17,8 +17,10 @@ from rich import print as rprint
|
|||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
from sqlmodel import select
|
||||
|
||||
from langflow.main import setup_app
|
||||
from langflow.services.database.models.folder.utils import create_default_folder_if_it_doesnt_exist
|
||||
from langflow.services.database.utils import session_getter
|
||||
from langflow.services.deps import get_db_service
|
||||
from langflow.services.utils import initialize_services
|
||||
|
|
@ -432,17 +434,57 @@ def superuser(
|
|||
# Verify that the superuser was created
|
||||
from langflow.services.database.models.user.model import User
|
||||
|
||||
user: User = session.query(User).filter(User.username == username).first()
|
||||
user: User = session.exec(select(User).where(User.username == username)).first()
|
||||
if user is None or not user.is_superuser:
|
||||
typer.echo("Superuser creation failed.")
|
||||
return
|
||||
|
||||
# Now create the first folder for the user
|
||||
result = create_default_folder_if_it_doesnt_exist(session, user.id)
|
||||
if result:
|
||||
typer.echo("Default folder created successfully.")
|
||||
else:
|
||||
raise RuntimeError("Could not create default folder.")
|
||||
typer.echo("Superuser created successfully.")
|
||||
|
||||
else:
|
||||
typer.echo("Superuser creation failed.")
|
||||
|
||||
|
||||
# command to copy the langflow database from the cache to the current directory
|
||||
# because now the database is stored per installation
|
||||
@app.command()
|
||||
def copy_db():
|
||||
"""
|
||||
Copy the database files to the current directory.
|
||||
|
||||
This function copies the 'langflow.db' and 'langflow-pre.db' files from the cache directory to the current directory.
|
||||
If the files exist in the cache directory, they will be copied to the same directory as this script (__main__.py).
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
import shutil
|
||||
|
||||
from platformdirs import user_cache_dir
|
||||
|
||||
cache_dir = Path(user_cache_dir("langflow"))
|
||||
db_path = cache_dir / "langflow.db"
|
||||
pre_db_path = cache_dir / "langflow-pre.db"
|
||||
# It should be copied to the current directory
|
||||
# this file is __main__.py and it should be in the same directory as the database
|
||||
destination_folder = Path(__file__).parent
|
||||
if db_path.exists():
|
||||
shutil.copy(db_path, destination_folder)
|
||||
typer.echo(f"Database copied to {destination_folder}")
|
||||
else:
|
||||
typer.echo("Database not found in the cache directory.")
|
||||
if pre_db_path.exists():
|
||||
shutil.copy(pre_db_path, destination_folder)
|
||||
typer.echo(f"Pre-release database copied to {destination_folder}")
|
||||
else:
|
||||
typer.echo("Pre-release database not found in the cache directory.")
|
||||
|
||||
|
||||
@app.command()
|
||||
def migration(
|
||||
test: bool = typer.Option(True, help="Run migrations in test mode."),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
"""Add Folder table
|
||||
|
||||
Revision ID: 012fb73ac359
|
||||
Revises: c153816fd85f
|
||||
Create Date: 2024-05-07 12:52:16.954691
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "012fb73ac359"
|
||||
down_revision: Union[str, None] = "c153816fd85f"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
table_names = inspector.get_table_names()
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
if "folder" not in table_names:
|
||||
op.create_table(
|
||||
"folder",
|
||||
sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
||||
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
|
||||
sa.Column("parent_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
|
||||
sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["parent_id"],
|
||||
["folder.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["user.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
indexes = inspector.get_indexes("folder")
|
||||
if "ix_folder_name" not in [index["name"] for index in indexes]:
|
||||
with op.batch_alter_table("folder", schema=None) as batch_op:
|
||||
batch_op.create_index(batch_op.f("ix_folder_name"), ["name"], unique=False)
|
||||
|
||||
if "folder_id" not in inspector.get_columns("flow"):
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("folder_id", sqlmodel.sql.sqltypes.GUID(), nullable=True))
|
||||
batch_op.create_foreign_key("flow_folder_id_fkey", "folder", ["folder_id"], ["id"])
|
||||
batch_op.drop_column("folder")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
table_names = inspector.get_table_names()
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
if "folder_id" in inspector.get_columns("flow"):
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("folder", sa.VARCHAR(), nullable=True))
|
||||
batch_op.drop_constraint("flow_folder_id_fkey", type_="foreignkey")
|
||||
batch_op.drop_column("folder_id")
|
||||
|
||||
indexes = inspector.get_indexes("folder")
|
||||
if "ix_folder_name" in [index["name"] for index in indexes]:
|
||||
with op.batch_alter_table("folder", schema=None) as batch_op:
|
||||
batch_op.drop_index(batch_op.f("ix_folder_name"))
|
||||
|
||||
if "folder" in table_names:
|
||||
op.drop_table("folder")
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
"""Add missing index
|
||||
|
||||
Revision ID: 29fe8f1f806b
|
||||
Revises: 012fb73ac359
|
||||
Create Date: 2024-05-21 09:23:48.772367
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
revision: str = "29fe8f1f806b"
|
||||
down_revision: Union[str, None] = "012fb73ac359"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
indexes = inspector.get_indexes("flow")
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
indexes_names = [index["name"] for index in indexes]
|
||||
if "ix_flow_folder_id" not in indexes_names:
|
||||
batch_op.create_index(batch_op.f("ix_flow_folder_id"), ["folder_id"], unique=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
indexes = inspector.get_indexes("flow")
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
indexes_names = [index["name"] for index in indexes]
|
||||
if "ix_flow_folder_id" in indexes_names:
|
||||
batch_op.drop_index(batch_op.f("ix_flow_folder_id"))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -13,6 +13,7 @@ from langflow.api.v1 import (
|
|||
users_router,
|
||||
validate_router,
|
||||
variables_router,
|
||||
folders_router,
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
|
|
@ -29,3 +30,4 @@ router.include_router(login_router)
|
|||
router.include_router(variables_router)
|
||||
router.include_router(files_router)
|
||||
router.include_router(monitor_router)
|
||||
router.include_router(folders_router)
|
||||
|
|
|
|||
|
|
@ -140,7 +140,10 @@ def get_file_path_value(file_path):
|
|||
# If the path is not in the cache dir, return empty string
|
||||
# This is to prevent access to files outside the cache dir
|
||||
# If the path is not a file, return empty string
|
||||
if not path.exists() or not str(path).startswith(user_cache_dir("langflow", "langflow")):
|
||||
if not str(path).startswith(user_cache_dir("langflow", "langflow")):
|
||||
return ""
|
||||
|
||||
if not path.exists():
|
||||
return ""
|
||||
return file_path
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from langflow.api.v1.store import router as store_router
|
|||
from langflow.api.v1.users import router as users_router
|
||||
from langflow.api.v1.validate import router as validate_router
|
||||
from langflow.api.v1.variable import router as variables_router
|
||||
from langflow.api.v1.folders import router as folders_router
|
||||
|
||||
__all__ = [
|
||||
"chat_router",
|
||||
|
|
@ -22,4 +23,5 @@ __all__ = [
|
|||
"variables_router",
|
||||
"monitor_router",
|
||||
"files_router",
|
||||
"folders_router",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from langchain.schema import AgentAction, AgentFinish
|
||||
from langchain_core.callbacks.base import AsyncCallbackHandler
|
||||
from loguru import logger
|
||||
|
||||
from langflow.api.v1.schemas import ChatResponse, PromptResponse
|
||||
from langflow.services.deps import get_chat_service, get_socket_service
|
||||
from langflow.utils.util import remove_ansi_escape_codes
|
||||
from langchain_core.agents import AgentAction, AgentFinish
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.socket.service import SocketIOService
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ from langflow.services.deps import get_chat_service, get_session, get_session_se
|
|||
from langflow.services.monitor.utils import log_vertex_build
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.vertex.types import ChatVertex
|
||||
from langflow.graph.vertex.types import InterfaceVertex
|
||||
from langflow.services.session.service import SessionService
|
||||
|
||||
router = APIRouter(tags=["Chat"])
|
||||
|
|
@ -53,7 +53,7 @@ async def try_running_celery_task(vertex, user_id):
|
|||
|
||||
@router.post("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||
async def retrieve_vertices_order(
|
||||
flow_id: str,
|
||||
flow_id: uuid.UUID,
|
||||
data: Optional[Annotated[Optional[FlowDataRequest], Body(embed=True)]] = None,
|
||||
stop_component_id: Optional[str] = None,
|
||||
start_component_id: Optional[str] = None,
|
||||
|
|
@ -78,12 +78,13 @@ async def retrieve_vertices_order(
|
|||
HTTPException: If there is an error checking the build status.
|
||||
"""
|
||||
try:
|
||||
flow_id_str = str(flow_id)
|
||||
# First, we need to check if the flow_id is in the cache
|
||||
if not data:
|
||||
graph = await build_and_cache_graph_from_db(flow_id=flow_id, session=session, chat_service=chat_service)
|
||||
graph = await build_and_cache_graph_from_db(flow_id=flow_id_str, session=session, chat_service=chat_service)
|
||||
else:
|
||||
graph = await build_and_cache_graph_from_data(
|
||||
flow_id=flow_id, graph_data=data.model_dump(), chat_service=chat_service
|
||||
flow_id=flow_id_str, graph_data=data.model_dump(), chat_service=chat_service
|
||||
)
|
||||
graph.validate_stream()
|
||||
if stop_component_id or start_component_id:
|
||||
|
|
@ -119,7 +120,7 @@ async def retrieve_vertices_order(
|
|||
|
||||
@router.post("/build/{flow_id}/vertices/{vertex_id}")
|
||||
async def build_vertex(
|
||||
flow_id: str,
|
||||
flow_id: uuid.UUID,
|
||||
vertex_id: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
inputs: Annotated[Optional[InputValueRequest], Body(embed=True)] = None,
|
||||
|
|
@ -143,27 +144,25 @@ async def build_vertex(
|
|||
HTTPException: If there is an error building the vertex.
|
||||
|
||||
"""
|
||||
flow_id_str = str(flow_id)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
next_runnable_vertices = []
|
||||
top_level_vertices = []
|
||||
try:
|
||||
start_time = time.perf_counter()
|
||||
cache = await chat_service.get_cache(flow_id)
|
||||
cache = await chat_service.get_cache(flow_id_str)
|
||||
if not cache:
|
||||
# If there's no cache
|
||||
logger.warning(f"No cache found for {flow_id}. Building graph starting at {vertex_id}")
|
||||
logger.warning(f"No cache found for {flow_id_str}. Building graph starting at {vertex_id}")
|
||||
graph = await build_and_cache_graph_from_db(
|
||||
flow_id=flow_id, session=next(get_session()), chat_service=chat_service
|
||||
flow_id=flow_id_str, session=next(get_session()), chat_service=chat_service
|
||||
)
|
||||
else:
|
||||
graph = cache.get("result")
|
||||
result_data_response = ResultDataResponse(results={})
|
||||
duration = ""
|
||||
vertex = graph.get_vertex(vertex_id)
|
||||
try:
|
||||
lock = chat_service._cache_locks[flow_id]
|
||||
set_cache_coro = partial(chat_service.set_cache, flow_id=flow_id)
|
||||
lock = chat_service._cache_locks[flow_id_str]
|
||||
set_cache_coro = partial(chat_service.set_cache, flow_id=flow_id_str)
|
||||
(
|
||||
next_runnable_vertices,
|
||||
top_level_vertices,
|
||||
|
|
@ -189,13 +188,13 @@ async def build_vertex(
|
|||
artifacts = {}
|
||||
# If there's an error building the vertex
|
||||
# we need to clear the cache
|
||||
await chat_service.clear_cache(flow_id)
|
||||
await chat_service.clear_cache(flow_id_str)
|
||||
|
||||
# Log the vertex build
|
||||
if not vertex.will_stream:
|
||||
background_tasks.add_task(
|
||||
log_vertex_build,
|
||||
flow_id=flow_id,
|
||||
flow_id=flow_id_str,
|
||||
vertex_id=vertex_id,
|
||||
valid=valid,
|
||||
params=params,
|
||||
|
|
@ -212,7 +211,7 @@ async def build_vertex(
|
|||
inactivated_vertices = list(graph.inactivated_vertices)
|
||||
graph.reset_inactivated_vertices()
|
||||
graph.reset_activated_vertices()
|
||||
await chat_service.set_cache(flow_id, graph)
|
||||
await chat_service.set_cache(flow_id_str, graph)
|
||||
|
||||
# graph.stop_vertex tells us if the user asked
|
||||
# to stop the build of the graph at a certain vertex
|
||||
|
|
@ -240,7 +239,7 @@ async def build_vertex(
|
|||
|
||||
@router.get("/build/{flow_id}/{vertex_id}/stream", response_class=StreamingResponse)
|
||||
async def build_vertex_stream(
|
||||
flow_id: str,
|
||||
flow_id: uuid.UUID,
|
||||
vertex_id: str,
|
||||
session_id: Optional[str] = None,
|
||||
chat_service: "ChatService" = Depends(get_chat_service),
|
||||
|
|
@ -272,23 +271,24 @@ async def build_vertex_stream(
|
|||
HTTPException: If an error occurs while building the vertex.
|
||||
"""
|
||||
try:
|
||||
flow_id_str = str(flow_id)
|
||||
|
||||
async def stream_vertex():
|
||||
try:
|
||||
if not session_id:
|
||||
cache = await chat_service.get_cache(flow_id)
|
||||
cache = await chat_service.get_cache(flow_id_str)
|
||||
if not cache:
|
||||
# If there's no cache
|
||||
raise ValueError(f"No cache found for {flow_id}.")
|
||||
raise ValueError(f"No cache found for {flow_id_str}.")
|
||||
else:
|
||||
graph = cache.get("result")
|
||||
else:
|
||||
session_data = await session_service.load_session(session_id, flow_id=flow_id)
|
||||
session_data = await session_service.load_session(session_id, flow_id=flow_id_str)
|
||||
graph, artifacts = session_data if session_data else (None, None)
|
||||
if not graph:
|
||||
raise ValueError(f"No graph found for {flow_id}.")
|
||||
raise ValueError(f"No graph found for {flow_id_str}.")
|
||||
|
||||
vertex: "ChatVertex" = graph.get_vertex(vertex_id)
|
||||
vertex: "InterfaceVertex" = graph.get_vertex(vertex_id)
|
||||
if not hasattr(vertex, "stream"):
|
||||
raise ValueError(f"Vertex {vertex_id} does not support streaming")
|
||||
if isinstance(vertex._built_result, str) and vertex._built_result:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from http import HTTPStatus
|
||||
from typing import Annotated, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
import sqlalchemy as sa
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, UploadFile, status
|
||||
|
|
@ -18,9 +19,7 @@ from langflow.api.v1.schemas import (
|
|||
UploadFileResponse,
|
||||
)
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.interface.custom.directory_reader import DirectoryReader
|
||||
from langflow.interface.custom.utils import build_custom_component_template
|
||||
from langflow.processing.process import process_tweaks, run_graph_internal
|
||||
from langflow.schema.graph import Tweaks
|
||||
|
|
@ -54,7 +53,7 @@ def get_all(
|
|||
@router.post("/run/{flow_id}", response_model=RunResponse, response_model_exclude_none=True)
|
||||
async def simplified_run_flow(
|
||||
db: Annotated[Session, Depends(get_session)],
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
input_request: SimplifiedAPIRequest = SimplifiedAPIRequest(),
|
||||
stream: bool = False,
|
||||
api_key_user: User = Depends(api_key_security),
|
||||
|
|
@ -111,26 +110,26 @@ async def simplified_run_flow(
|
|||
session_id = input_request.session_id
|
||||
|
||||
try:
|
||||
task_result: List[RunOutputs] = []
|
||||
flow_id_str = str(flow_id)
|
||||
artifacts = {}
|
||||
if input_request.session_id:
|
||||
session_data = await session_service.load_session(input_request.session_id, flow_id=flow_id)
|
||||
session_data = await session_service.load_session(input_request.session_id, flow_id=flow_id_str)
|
||||
graph, artifacts = session_data if session_data else (None, None)
|
||||
if graph is None:
|
||||
raise ValueError(f"Session {input_request.session_id} not found")
|
||||
else:
|
||||
# Get the flow that matches the flow_id and belongs to the user
|
||||
# flow = session.query(Flow).filter(Flow.id == flow_id).filter(Flow.user_id == api_key_user.id).first()
|
||||
flow = db.exec(select(Flow).where(Flow.id == flow_id).where(Flow.user_id == api_key_user.id)).first()
|
||||
flow = db.exec(select(Flow).where(Flow.id == flow_id_str).where(Flow.user_id == api_key_user.id)).first()
|
||||
if flow is None:
|
||||
raise ValueError(f"Flow {flow_id} not found")
|
||||
raise ValueError(f"Flow {flow_id_str} not found")
|
||||
|
||||
if flow.data is None:
|
||||
raise ValueError(f"Flow {flow_id} has no data")
|
||||
raise ValueError(f"Flow {flow_id_str} has no data")
|
||||
graph_data = flow.data
|
||||
|
||||
graph_data = process_tweaks(graph_data, input_request.tweaks or {}, stream=stream)
|
||||
graph = Graph.from_payload(graph_data, flow_id=flow_id, user_id=str(api_key_user.id))
|
||||
graph = Graph.from_payload(graph_data, flow_id=flow_id_str, user_id=str(api_key_user.id))
|
||||
inputs = [
|
||||
InputValueRequest(components=[], input_value=input_request.input_value, type=input_request.input_type)
|
||||
]
|
||||
|
|
@ -153,7 +152,7 @@ async def simplified_run_flow(
|
|||
]
|
||||
task_result, session_id = await run_graph_internal(
|
||||
graph=graph,
|
||||
flow_id=flow_id,
|
||||
flow_id=flow_id_str,
|
||||
session_id=input_request.session_id,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
|
|
@ -166,12 +165,12 @@ async def simplified_run_flow(
|
|||
except sa.exc.StatementError as exc:
|
||||
# StatementError('(builtins.ValueError) badly formed hexadecimal UUID string')
|
||||
if "badly formed hexadecimal UUID string" in str(exc):
|
||||
logger.error(f"Flow ID {flow_id} is not a valid UUID")
|
||||
logger.error(f"Flow ID {flow_id_str} is not a valid UUID")
|
||||
# This means the Flow ID is not a valid UUID which means it can't find the flow
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
except ValueError as exc:
|
||||
if f"Flow {flow_id} not found" in str(exc):
|
||||
logger.error(f"Flow {flow_id} not found")
|
||||
if f"Flow {flow_id_str} not found" in str(exc):
|
||||
logger.error(f"Flow {flow_id_str} not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
elif f"Session {session_id} not found" in str(exc):
|
||||
logger.error(f"Session {session_id} not found")
|
||||
|
|
@ -187,7 +186,7 @@ async def simplified_run_flow(
|
|||
@router.post("/run/advanced/{flow_id}", response_model=RunResponse, response_model_exclude_none=True)
|
||||
async def experimental_run_flow(
|
||||
session: Annotated[Session, Depends(get_session)],
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
inputs: Optional[List[InputValueRequest]] = [InputValueRequest(components=[], input_value="")],
|
||||
outputs: Optional[List[str]] = [],
|
||||
tweaks: Annotated[Optional[Tweaks], Body(embed=True)] = None, # noqa: F821
|
||||
|
|
@ -235,31 +234,33 @@ async def experimental_run_flow(
|
|||
This endpoint facilitates complex flow executions with customized inputs, outputs, and configurations, catering to diverse application requirements.
|
||||
"""
|
||||
try:
|
||||
flow_id_str = str(flow_id)
|
||||
if outputs is None:
|
||||
outputs = []
|
||||
|
||||
task_result: List[RunOutputs] = []
|
||||
artifacts = {}
|
||||
if session_id:
|
||||
session_data = await session_service.load_session(session_id, flow_id=flow_id)
|
||||
session_data = await session_service.load_session(session_id, flow_id=flow_id_str)
|
||||
graph, artifacts = session_data if session_data else (None, None)
|
||||
if graph is None:
|
||||
raise ValueError(f"Session {session_id} not found")
|
||||
else:
|
||||
# Get the flow that matches the flow_id and belongs to the user
|
||||
# flow = session.query(Flow).filter(Flow.id == flow_id).filter(Flow.user_id == api_key_user.id).first()
|
||||
flow = session.exec(select(Flow).where(Flow.id == flow_id).where(Flow.user_id == api_key_user.id)).first()
|
||||
flow = session.exec(
|
||||
select(Flow).where(Flow.id == flow_id_str).where(Flow.user_id == api_key_user.id)
|
||||
).first()
|
||||
if flow is None:
|
||||
raise ValueError(f"Flow {flow_id} not found")
|
||||
raise ValueError(f"Flow {flow_id_str} not found")
|
||||
|
||||
if flow.data is None:
|
||||
raise ValueError(f"Flow {flow_id} has no data")
|
||||
raise ValueError(f"Flow {flow_id_str} has no data")
|
||||
graph_data = flow.data
|
||||
graph_data = process_tweaks(graph_data, tweaks or {})
|
||||
graph = Graph.from_payload(graph_data, flow_id=flow_id)
|
||||
graph = Graph.from_payload(graph_data, flow_id=flow_id_str)
|
||||
task_result, session_id = await run_graph_internal(
|
||||
graph=graph,
|
||||
flow_id=flow_id,
|
||||
flow_id=flow_id_str,
|
||||
session_id=session_id,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
|
|
@ -272,12 +273,12 @@ async def experimental_run_flow(
|
|||
except sa.exc.StatementError as exc:
|
||||
# StatementError('(builtins.ValueError) badly formed hexadecimal UUID string')
|
||||
if "badly formed hexadecimal UUID string" in str(exc):
|
||||
logger.error(f"Flow ID {flow_id} is not a valid UUID")
|
||||
logger.error(f"Flow ID {flow_id_str} is not a valid UUID")
|
||||
# This means the Flow ID is not a valid UUID which means it can't find the flow
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
except ValueError as exc:
|
||||
if f"Flow {flow_id} not found" in str(exc):
|
||||
logger.error(f"Flow {flow_id} not found")
|
||||
if f"Flow {flow_id_str} not found" in str(exc):
|
||||
logger.error(f"Flow {flow_id_str} not found")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
elif f"Session {session_id} not found" in str(exc):
|
||||
logger.error(f"Session {session_id} not found")
|
||||
|
|
@ -357,13 +358,14 @@ async def get_task_status(task_id: str):
|
|||
)
|
||||
async def create_upload_file(
|
||||
file: UploadFile,
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
):
|
||||
try:
|
||||
file_path = save_uploaded_file(file, folder_name=flow_id)
|
||||
flow_id_str = str(flow_id)
|
||||
file_path = save_uploaded_file(file, folder_name=flow_id_str)
|
||||
|
||||
return UploadFileResponse(
|
||||
flowId=flow_id,
|
||||
flowId=flow_id_str,
|
||||
file_path=file_path,
|
||||
)
|
||||
except Exception as exc:
|
||||
|
|
@ -400,23 +402,6 @@ async def custom_component(
|
|||
return built_frontend_node
|
||||
|
||||
|
||||
@router.post("/custom_component/reload", status_code=HTTPStatus.OK)
|
||||
async def reload_custom_component(path: str, user: User = Depends(get_current_active_user)):
|
||||
from langflow.interface.custom.utils import build_custom_component_template
|
||||
|
||||
try:
|
||||
reader = DirectoryReader("")
|
||||
valid, content = reader.process_file(path)
|
||||
if not valid:
|
||||
raise ValueError(content)
|
||||
|
||||
extractor = CustomComponent(code=content)
|
||||
frontend_node, _ = build_custom_component_template(extractor, user_id=user.id)
|
||||
return frontend_node
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
|
||||
@router.post("/custom_component/update", status_code=HTTPStatus.OK)
|
||||
async def custom_component_update(
|
||||
code_request: UpdateCustomComponentRequest,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import hashlib
|
||||
from http import HTTPStatus
|
||||
from io import BytesIO
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
|
@ -20,38 +21,41 @@ router = APIRouter(tags=["Files"], prefix="/files")
|
|||
# then finds it in the database and returns it while
|
||||
# using the current user as the owner
|
||||
def get_flow_id(
|
||||
flow_id: str,
|
||||
flow_id: UUID,
|
||||
current_user=Depends(get_current_active_user),
|
||||
session=Depends(get_session),
|
||||
):
|
||||
flow_id_str = str(flow_id)
|
||||
# AttributeError: 'SelectOfScalar' object has no attribute 'first'
|
||||
flow = session.get(Flow, flow_id)
|
||||
flow = session.get(Flow, flow_id_str)
|
||||
if not flow:
|
||||
raise HTTPException(status_code=404, detail="Flow not found")
|
||||
if flow.user_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="You don't have access to this flow")
|
||||
return flow_id
|
||||
return flow_id_str
|
||||
|
||||
|
||||
@router.post("/upload/{flow_id}", status_code=HTTPStatus.CREATED)
|
||||
async def upload_file(
|
||||
file: UploadFile,
|
||||
flow_id: str = Depends(get_flow_id),
|
||||
flow_id: UUID = Depends(get_flow_id),
|
||||
storage_service: StorageService = Depends(get_storage_service),
|
||||
):
|
||||
try:
|
||||
flow_id_str = str(flow_id)
|
||||
file_content = await file.read()
|
||||
file_name = file.filename or hashlib.sha256(file_content).hexdigest()
|
||||
folder = flow_id
|
||||
folder = flow_id_str
|
||||
await storage_service.save_file(flow_id=folder, file_name=file_name, data=file_content)
|
||||
return UploadFileResponse(flowId=flow_id, file_path=f"{folder}/{file_name}")
|
||||
return UploadFileResponse(flowId=flow_id_str, file_path=f"{folder}/{file_name}")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/download/{flow_id}/{file_name}")
|
||||
async def download_file(file_name: str, flow_id: str, storage_service: StorageService = Depends(get_storage_service)):
|
||||
async def download_file(file_name: str, flow_id: UUID, storage_service: StorageService = Depends(get_storage_service)):
|
||||
try:
|
||||
flow_id_str = str(flow_id)
|
||||
extension = file_name.split(".")[-1]
|
||||
|
||||
if not extension:
|
||||
|
|
@ -62,7 +66,7 @@ async def download_file(file_name: str, flow_id: str, storage_service: StorageSe
|
|||
if not content_type:
|
||||
raise HTTPException(status_code=500, detail=f"Content type not found for extension {extension}")
|
||||
|
||||
file_content = await storage_service.get_file(flow_id=flow_id, file_name=file_name)
|
||||
file_content = await storage_service.get_file(flow_id=flow_id_str, file_name=file_name)
|
||||
headers = {
|
||||
"Content-Disposition": f"attachment; filename={file_name} filename*=UTF-8''{file_name}",
|
||||
"Content-Type": "application/octet-stream",
|
||||
|
|
@ -74,9 +78,10 @@ async def download_file(file_name: str, flow_id: str, storage_service: StorageSe
|
|||
|
||||
|
||||
@router.get("/images/{flow_id}/{file_name}")
|
||||
async def download_image(file_name: str, flow_id: str, storage_service: StorageService = Depends(get_storage_service)):
|
||||
async def download_image(file_name: str, flow_id: UUID, storage_service: StorageService = Depends(get_storage_service)):
|
||||
try:
|
||||
extension = file_name.split(".")[-1]
|
||||
flow_id_str = str(flow_id)
|
||||
|
||||
if not extension:
|
||||
raise HTTPException(status_code=500, detail=f"Extension not found for file {file_name}")
|
||||
|
|
@ -88,7 +93,7 @@ async def download_image(file_name: str, flow_id: str, storage_service: StorageS
|
|||
elif not content_type.startswith("image"):
|
||||
raise HTTPException(status_code=500, detail=f"Content type {content_type} is not an image")
|
||||
|
||||
file_content = await storage_service.get_file(flow_id=flow_id, file_name=file_name)
|
||||
file_content = await storage_service.get_file(flow_id=flow_id_str, file_name=file_name)
|
||||
return StreamingResponse(BytesIO(file_content), media_type=content_type)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
@ -96,10 +101,11 @@ async def download_image(file_name: str, flow_id: str, storage_service: StorageS
|
|||
|
||||
@router.get("/list/{flow_id}")
|
||||
async def list_files(
|
||||
flow_id: str = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
):
|
||||
try:
|
||||
files = await storage_service.list_files(flow_id=flow_id)
|
||||
flow_id_str = str(flow_id)
|
||||
files = await storage_service.list_files(flow_id=flow_id_str)
|
||||
return {"files": files}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
@ -107,10 +113,11 @@ async def list_files(
|
|||
|
||||
@router.delete("/delete/{flow_id}/{file_name}")
|
||||
async def delete_file(
|
||||
file_name: str, flow_id: str = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
file_name: str, flow_id: UUID = Depends(get_flow_id), storage_service: StorageService = Depends(get_storage_service)
|
||||
):
|
||||
try:
|
||||
await storage_service.delete_file(flow_id=flow_id, file_name=file_name)
|
||||
flow_id_str = str(flow_id)
|
||||
await storage_service.delete_file(flow_id=flow_id_str, file_name=file_name)
|
||||
return {"message": f"File {file_name} deleted successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ import orjson
|
|||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from loguru import logger
|
||||
from sqlmodel import Session, select
|
||||
from sqlmodel import Session, col, select
|
||||
|
||||
from langflow.api.utils import remove_api_keys, validate_is_component
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListRead
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListIds, FlowListRead
|
||||
from langflow.initial_setup.setup import STARTER_FOLDER_NAME
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.database.models.flow import Flow, FlowCreate, FlowRead, FlowUpdate
|
||||
from langflow.services.database.models.folder.constants import DEFAULT_FOLDER_NAME
|
||||
from langflow.services.database.models.folder.model import Folder
|
||||
from langflow.services.database.models.user.model import User
|
||||
from langflow.services.deps import get_session, get_settings_service
|
||||
from langflow.services.settings.service import SettingsService
|
||||
|
|
@ -35,6 +37,11 @@ def create_flow(
|
|||
db_flow = Flow.model_validate(flow, from_attributes=True)
|
||||
db_flow.updated_at = datetime.now(timezone.utc)
|
||||
|
||||
if db_flow.folder_id is None:
|
||||
default_folder = session.exec(select(Folder).where(Folder.name == DEFAULT_FOLDER_NAME)).first()
|
||||
if default_folder:
|
||||
db_flow.folder_id = default_folder.id
|
||||
|
||||
session.add(db_flow)
|
||||
session.commit()
|
||||
session.refresh(db_flow)
|
||||
|
|
@ -64,12 +71,9 @@ def read_flows(
|
|||
flow_ids = [flow.id for flow in flows]
|
||||
# with the session get the flows that DO NOT have a user_id
|
||||
try:
|
||||
example_flows = session.exec(
|
||||
select(Flow).where(
|
||||
Flow.user_id == None, # noqa
|
||||
Flow.folder == STARTER_FOLDER_NAME,
|
||||
)
|
||||
).all()
|
||||
folder = session.exec(select(Folder).where(Folder.name == STARTER_FOLDER_NAME)).first()
|
||||
|
||||
example_flows = folder.flows if folder else []
|
||||
for example_flow in example_flows:
|
||||
if example_flow.id not in flow_ids:
|
||||
flows.append(example_flow) # type: ignore
|
||||
|
|
@ -129,6 +133,10 @@ def update_flow(
|
|||
if value is not None:
|
||||
setattr(db_flow, key, value)
|
||||
db_flow.updated_at = datetime.now(timezone.utc)
|
||||
if db_flow.folder_id is None:
|
||||
default_folder = session.exec(select(Folder).where(Folder.name == DEFAULT_FOLDER_NAME)).first()
|
||||
if default_folder:
|
||||
db_flow.folder_id = default_folder.id
|
||||
session.add(db_flow)
|
||||
session.commit()
|
||||
session.refresh(db_flow)
|
||||
|
|
@ -208,3 +216,31 @@ async def download_file(
|
|||
"""Download all flows as a file."""
|
||||
flows = read_flows(current_user=current_user, session=session, settings_service=settings_service)
|
||||
return FlowListRead(flows=flows)
|
||||
|
||||
|
||||
@router.post("/multiple_delete/")
|
||||
async def delete_multiple_flows(
|
||||
flow_ids: FlowListIds, user: User = Depends(get_current_active_user), db: Session = Depends(get_session)
|
||||
):
|
||||
"""
|
||||
Delete multiple flows by their IDs.
|
||||
|
||||
Args:
|
||||
flow_ids (List[str]): The list of flow IDs to delete.
|
||||
user (User, optional): The user making the request. Defaults to the current active user.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the number of flows deleted.
|
||||
|
||||
"""
|
||||
try:
|
||||
deleted_flows = db.exec(
|
||||
select(Flow).where(col(Flow.id).in_(flow_ids.flow_ids)).where(Flow.user_id == user.id)
|
||||
).all()
|
||||
for flow in deleted_flows:
|
||||
db.delete(flow)
|
||||
db.commit()
|
||||
return {"deleted": len(deleted_flows)}
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
|
|
|||
239
src/backend/base/langflow/api/v1/folders.py
Normal file
239
src/backend/base/langflow/api/v1/folders.py
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
import orjson
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFile, status
|
||||
from sqlalchemy import or_, update
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from langflow.api.v1.flows import create_flows
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListReadWithFolderName
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.database.models.flow.model import Flow, FlowCreate, FlowRead
|
||||
from langflow.services.database.models.folder.constants import DEFAULT_FOLDER_NAME
|
||||
from langflow.services.database.models.folder.model import (
|
||||
Folder,
|
||||
FolderCreate,
|
||||
FolderRead,
|
||||
FolderReadWithFlows,
|
||||
FolderUpdate,
|
||||
)
|
||||
from langflow.services.database.models.user.model import User
|
||||
from langflow.services.deps import get_session
|
||||
|
||||
router = APIRouter(prefix="/folders", tags=["Folders"])
|
||||
|
||||
|
||||
@router.post("/", response_model=FolderRead, status_code=201)
|
||||
def create_folder(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
folder: FolderCreate,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
new_folder = Folder.model_validate(folder, from_attributes=True)
|
||||
new_folder.user_id = current_user.id
|
||||
|
||||
folder_results = session.exec(
|
||||
select(Folder).where(
|
||||
Folder.name.like(f"{new_folder.name}%"), # type: ignore
|
||||
Folder.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
existing_folder_names = [folder.name for folder in folder_results]
|
||||
|
||||
if existing_folder_names:
|
||||
new_folder.name = f"{new_folder.name} ({len(existing_folder_names) + 1})"
|
||||
|
||||
session.add(new_folder)
|
||||
session.commit()
|
||||
session.refresh(new_folder)
|
||||
|
||||
if folder.components_list:
|
||||
update_statement_components = (
|
||||
update(Flow).where(Flow.id.in_(folder.components_list)).values(folder_id=new_folder.id) # type: ignore
|
||||
)
|
||||
session.exec(update_statement_components) # type: ignore
|
||||
session.commit()
|
||||
|
||||
if folder.flows_list:
|
||||
update_statement_flows = update(Flow).where(Flow.id.in_(folder.flows_list)).values(folder_id=new_folder.id) # type: ignore
|
||||
session.exec(update_statement_flows) # type: ignore
|
||||
session.commit()
|
||||
|
||||
return new_folder
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/", response_model=List[FolderRead], status_code=200)
|
||||
def read_folders(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
folders = session.exec(
|
||||
select(Folder).where(
|
||||
or_(Folder.user_id == current_user.id, Folder.user_id == None) # type: ignore # noqa: E711
|
||||
)
|
||||
).all()
|
||||
return folders
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/{folder_id}", response_model=FolderReadWithFlows, status_code=200)
|
||||
def read_folder(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
folder_id: UUID,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
folder = session.exec(select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id)).first()
|
||||
if not folder:
|
||||
raise HTTPException(status_code=404, detail="Folder not found")
|
||||
return folder
|
||||
except Exception as e:
|
||||
if "No result found" in str(e):
|
||||
raise HTTPException(status_code=404, detail="Folder not found")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.patch("/{folder_id}", response_model=FolderRead, status_code=200)
|
||||
def update_folder(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
folder_id: UUID,
|
||||
folder: FolderUpdate, # Assuming FolderUpdate is a Pydantic model defining updatable fields
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
existing_folder = session.exec(
|
||||
select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id)
|
||||
).first()
|
||||
if not existing_folder:
|
||||
raise HTTPException(status_code=404, detail="Folder not found")
|
||||
folder_data = folder.model_dump(exclude_unset=True)
|
||||
for key, value in folder_data.items():
|
||||
if key != "components" and key != "flows":
|
||||
setattr(existing_folder, key, value)
|
||||
session.add(existing_folder)
|
||||
session.commit()
|
||||
session.refresh(existing_folder)
|
||||
|
||||
concat_folder_components = folder.components + folder.flows
|
||||
|
||||
flows_ids = session.exec(select(Flow.id).where(Flow.folder_id == existing_folder.id)).all()
|
||||
|
||||
excluded_flows = list(set(flows_ids) - set(concat_folder_components))
|
||||
|
||||
my_collection_folder = session.exec(select(Folder).where(Folder.name == DEFAULT_FOLDER_NAME)).first()
|
||||
if my_collection_folder:
|
||||
update_statement_my_collection = (
|
||||
update(Flow).where(Flow.id.in_(excluded_flows)).values(folder_id=my_collection_folder.id) # type: ignore
|
||||
)
|
||||
session.exec(update_statement_my_collection) # type: ignore
|
||||
session.commit()
|
||||
|
||||
if concat_folder_components:
|
||||
update_statement_components = (
|
||||
update(Flow).where(Flow.id.in_(concat_folder_components)).values(folder_id=existing_folder.id) # type: ignore
|
||||
)
|
||||
session.exec(update_statement_components) # type: ignore
|
||||
session.commit()
|
||||
|
||||
return existing_folder
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/{folder_id}", status_code=204)
|
||||
def delete_folder(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
folder_id: UUID,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
try:
|
||||
folder = session.exec(select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id)).first()
|
||||
if not folder:
|
||||
raise HTTPException(status_code=404, detail="Folder not found")
|
||||
session.delete(folder)
|
||||
session.commit()
|
||||
flows = session.exec(select(Flow).where(Flow.folder_id == folder_id, Folder.user_id == current_user.id)).all()
|
||||
for flow in flows:
|
||||
session.delete(flow)
|
||||
session.commit()
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/download/{folder_id}", response_model=FlowListReadWithFolderName, status_code=200)
|
||||
async def download_file(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
folder_id: UUID,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
"""Download all flows from folder."""
|
||||
try:
|
||||
folder = session.exec(select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id)).first()
|
||||
return folder
|
||||
except Exception as e:
|
||||
if "No result found" in str(e):
|
||||
raise HTTPException(status_code=404, detail="Folder not found")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/upload/", response_model=List[FlowRead], status_code=201)
|
||||
async def upload_file(
|
||||
*,
|
||||
session: Session = Depends(get_session),
|
||||
file: UploadFile = File(...),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
"""Upload flows from a file."""
|
||||
contents = await file.read()
|
||||
data = orjson.loads(contents)
|
||||
|
||||
if not data:
|
||||
raise HTTPException(status_code=400, detail="No flows found in the file")
|
||||
|
||||
folder_results = session.exec(
|
||||
select(Folder).where(
|
||||
Folder.name == data["folder_name"],
|
||||
Folder.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
existing_folder_names = [folder.name for folder in folder_results]
|
||||
|
||||
if existing_folder_names:
|
||||
data["folder_name"] = f"{data['folder_name']} ({len(existing_folder_names) + 1})"
|
||||
|
||||
folder = FolderCreate(name=data["folder_name"], description=data["folder_description"])
|
||||
|
||||
new_folder = Folder.model_validate(folder, from_attributes=True)
|
||||
new_folder.id = None
|
||||
new_folder.user_id = current_user.id
|
||||
session.add(new_folder)
|
||||
session.commit()
|
||||
session.refresh(new_folder)
|
||||
|
||||
del data["folder_name"]
|
||||
del data["folder_description"]
|
||||
|
||||
if "flows" in data:
|
||||
flow_list = FlowListCreate(flows=[FlowCreate(**flow) for flow in data["flows"]])
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="No flows found in the data")
|
||||
# Now we set the user_id for all flows
|
||||
for flow in flow_list.flows:
|
||||
flow.user_id = current_user.id
|
||||
flow.folder_id = new_folder.id
|
||||
|
||||
return create_flows(session=session, flow_list=flow_list, current_user=current_user)
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlmodel import Session
|
||||
|
||||
from langflow.api.v1.schemas import Token
|
||||
from langflow.services.auth.utils import (
|
||||
authenticate_user,
|
||||
|
|
@ -7,14 +9,10 @@ from langflow.services.auth.utils import (
|
|||
create_user_longterm_token,
|
||||
create_user_tokens,
|
||||
)
|
||||
from langflow.services.deps import (
|
||||
get_session,
|
||||
get_settings_service,
|
||||
get_variable_service,
|
||||
)
|
||||
from langflow.services.database.models.folder.utils import create_default_folder_if_it_doesnt_exist
|
||||
from langflow.services.deps import get_session, get_settings_service, get_variable_service
|
||||
from langflow.services.settings.manager import SettingsService
|
||||
from langflow.services.variable.service import VariableService
|
||||
from sqlmodel import Session
|
||||
|
||||
router = APIRouter(tags=["Login"])
|
||||
|
||||
|
|
@ -58,6 +56,8 @@ async def login_to_get_access_token(
|
|||
expires=auth_settings.ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
)
|
||||
variable_service.initialize_user_variables(user.id, db)
|
||||
# Create default folder for user if it doesn't exist
|
||||
create_default_folder_if_it_doesnt_exist(db, user.id)
|
||||
return tokens
|
||||
else:
|
||||
raise HTTPException(
|
||||
|
|
@ -86,6 +86,7 @@ async def auto_login(
|
|||
expires=None, # Set to None to make it a session cookie
|
||||
)
|
||||
variable_service.initialize_user_variables(user_id, db)
|
||||
create_default_folder_if_it_doesnt_exist(db, user_id)
|
||||
return tokens
|
||||
|
||||
raise HTTPException(
|
||||
|
|
@ -139,4 +140,3 @@ async def logout(response: Response):
|
|||
response.delete_cookie("refresh_token_lf")
|
||||
response.delete_cookie("access_token_lf")
|
||||
return {"message": "Logout successful"}
|
||||
return {"message": "Logout successful"}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.services.monitor.schema import VertexBuildMapModel
|
||||
from langflow.services.monitor.schema import (
|
||||
MessageModelResponse,
|
||||
TransactionModelResponse,
|
||||
VertexBuildMapModel,
|
||||
)
|
||||
from langflow.services.monitor.service import MonitorService
|
||||
|
||||
router = APIRouter(prefix="/monitor", tags=["Monitor"])
|
||||
|
|
@ -40,8 +43,9 @@ async def delete_vertex_builds(
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/messages")
|
||||
@router.get("/messages", response_model=List[MessageModelResponse])
|
||||
async def get_messages(
|
||||
flow_id: Optional[str] = Query(None),
|
||||
session_id: Optional[str] = Query(None),
|
||||
sender: Optional[str] = Query(None),
|
||||
sender_name: Optional[str] = Query(None),
|
||||
|
|
@ -49,25 +53,32 @@ async def get_messages(
|
|||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
return monitor_service.get_messages(
|
||||
df = monitor_service.get_messages(
|
||||
flow_id=flow_id,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
order_by=order_by,
|
||||
)
|
||||
dicts = df.to_dict(orient="records")
|
||||
return [MessageModelResponse(**d) for d in dicts]
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/transactions")
|
||||
@router.get("/transactions", response_model=List[TransactionModelResponse])
|
||||
async def get_transactions(
|
||||
source: Optional[str] = Query(None),
|
||||
target: Optional[str] = Query(None),
|
||||
status: Optional[str] = Query(None),
|
||||
order_by: Optional[str] = Query("timestamp"),
|
||||
flow_id: Optional[str] = Query(None),
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
return monitor_service.get_transactions(source=source, target=target, status=status, order_by=order_by)
|
||||
dicts = monitor_service.get_transactions(
|
||||
source=source, target=target, status=status, order_by=order_by, flow_id=flow_id
|
||||
)
|
||||
return [TransactionModelResponse(**d) for d in dicts]
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
|
|||
|
|
@ -139,10 +139,20 @@ class FlowListCreate(BaseModel):
|
|||
flows: List[FlowCreate]
|
||||
|
||||
|
||||
class FlowListIds(BaseModel):
|
||||
flow_ids: List[str]
|
||||
|
||||
|
||||
class FlowListRead(BaseModel):
|
||||
flows: List[FlowRead]
|
||||
|
||||
|
||||
class FlowListReadWithFolderName(BaseModel):
|
||||
flows: List[FlowRead]
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
class InitResponse(BaseModel):
|
||||
flowId: str
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from langflow.services.auth.utils import (
|
|||
get_password_hash,
|
||||
verify_password,
|
||||
)
|
||||
from langflow.services.database.models.folder.utils import create_default_folder_if_it_doesnt_exist
|
||||
from langflow.services.database.models.user import User, UserCreate, UserRead, UserUpdate
|
||||
from langflow.services.database.models.user.crud import get_user_by_id, update_user
|
||||
from langflow.services.deps import get_session, get_settings_service
|
||||
|
|
@ -36,6 +37,9 @@ def add_user(
|
|||
session.add(new_user)
|
||||
session.commit()
|
||||
session.refresh(new_user)
|
||||
folder = create_default_folder_if_it_doesnt_exist(session, new_user.id)
|
||||
if not folder:
|
||||
raise HTTPException(status_code=500, detail="Error creating default folder")
|
||||
except IntegrityError as e:
|
||||
session.rollback()
|
||||
raise HTTPException(status_code=400, detail="This username is unavailable.") from e
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ class ChatComponent(CustomComponent):
|
|||
sender: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
) -> list[Record]:
|
||||
|
||||
records = store_message(
|
||||
message,
|
||||
session_id=session_id,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
flow_id=self.graph.flow_id,
|
||||
)
|
||||
|
||||
self.status = records
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
from fastapi import HTTPException
|
||||
from langchain.prompts import PromptTemplate
|
||||
from loguru import logger
|
||||
|
||||
from langflow.api.v1.base import INVALID_NAMES, check_input_variables
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langflow.template.field.prompt import DefaultPromptField
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
|
||||
def validate_prompt(prompt_template: str, silent_errors: bool = False) -> list[str]:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
from copy import deepcopy
|
||||
|
||||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.schema import Record
|
||||
|
|
@ -27,19 +30,20 @@ def dict_values_to_string(d: dict) -> dict:
|
|||
dict: The dictionary with values converted to strings.
|
||||
"""
|
||||
# Do something similar to the above
|
||||
for key, value in d.items():
|
||||
d_copy = deepcopy(d)
|
||||
for key, value in d_copy.items():
|
||||
# it could be a list of records or documents or strings
|
||||
if isinstance(value, list):
|
||||
for i, item in enumerate(value):
|
||||
if isinstance(item, Record):
|
||||
d[key][i] = record_to_string(item)
|
||||
d_copy[key][i] = record_to_string(item)
|
||||
elif isinstance(item, Document):
|
||||
d[key][i] = document_to_string(item)
|
||||
d_copy[key][i] = document_to_string(item)
|
||||
elif isinstance(value, Record):
|
||||
d[key] = record_to_string(value)
|
||||
d_copy[key] = record_to_string(value)
|
||||
elif isinstance(value, Document):
|
||||
d[key] = document_to_string(value)
|
||||
return d
|
||||
d_copy[key] = document_to_string(value)
|
||||
return d_copy
|
||||
|
||||
|
||||
def document_to_string(document: Document) -> str:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from langchain.agents import AgentExecutor, create_json_agent
|
||||
from langchain.agents import AgentExecutor
|
||||
from langchain_community.agent_toolkits.json.toolkit import JsonToolkit
|
||||
|
||||
from langflow.field_typing import (
|
||||
BaseLanguageModel,
|
||||
)
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_community.agent_toolkits import create_json_agent
|
||||
|
||||
|
||||
class JsonAgentComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ from langchain.agents.agent import AgentExecutor
|
|||
from langchain.agents.agent_toolkits.conversational_retrieval.openai_functions import _get_default_system_message
|
||||
from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent
|
||||
from langchain.memory.token_buffer import ConversationTokenBufferMemory
|
||||
from langchain.prompts import SystemMessagePromptTemplate
|
||||
from langchain.prompts.chat import MessagesPlaceholder
|
||||
from langchain.schema.memory import BaseMemory
|
||||
from langchain.tools import Tool
|
||||
from langchain_community.chat_models import ChatOpenAI
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
from langflow.field_typing.range_spec import RangeSpec
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from pydantic.v1 import SecretStr
|
||||
from langchain_core.memory import BaseMemory
|
||||
from langchain_core.prompts import MessagesPlaceholder, SystemMessagePromptTemplate
|
||||
from langchain_core.tools import Tool
|
||||
|
||||
|
||||
class ConversationalAgent(CustomComponent):
|
||||
|
|
@ -57,9 +57,14 @@ class ConversationalAgent(CustomComponent):
|
|||
max_token_limit: int = 2000,
|
||||
temperature: float = 0.9,
|
||||
) -> AgentExecutor:
|
||||
if openai_api_key:
|
||||
api_key = SecretStr(openai_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
|
||||
llm = ChatOpenAI(
|
||||
model=model_name,
|
||||
api_key=openai_api_key,
|
||||
api_key=api_key,
|
||||
base_url=openai_api_base,
|
||||
max_tokens=max_token_limit,
|
||||
temperature=temperature,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain_community.embeddings import BedrockEmbeddings
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class AmazonBedrockEmeddingsComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from langchain.embeddings.base import Embeddings
|
||||
from langchain_community.embeddings import AzureOpenAIEmbeddings
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_openai import AzureOpenAIEmbeddings
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
|
||||
class AzureOpenAIEmbeddingsComponent(CustomComponent):
|
||||
|
|
@ -52,12 +52,16 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
|
|||
api_version: str,
|
||||
api_key: str,
|
||||
) -> Embeddings:
|
||||
if api_key:
|
||||
azure_api_key = SecretStr(api_key)
|
||||
else:
|
||||
azure_api_key = None
|
||||
try:
|
||||
embeddings = AzureOpenAIEmbeddings(
|
||||
azure_endpoint=azure_endpoint,
|
||||
azure_deployment=azure_deployment,
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
api_key=azure_api_key,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import List, Optional
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langchain_mistralai.embeddings import MistralAIEmbeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.field_typing import Embeddings, NestedDict
|
||||
from langflow.field_typing import Embeddings
|
||||
|
||||
|
||||
class MistralAIEmbeddingsComponent(CustomComponent):
|
||||
display_name = "MistralAI Embeddings"
|
||||
|
|
@ -37,11 +37,7 @@ class MistralAIEmbeddingsComponent(CustomComponent):
|
|||
"advanced": True,
|
||||
"value": 120,
|
||||
},
|
||||
"endpoint": {
|
||||
"display_name": "API Endpoint",
|
||||
"advanced": True,
|
||||
"value": "https://api.mistral.ai/v1/"
|
||||
}
|
||||
"endpoint": {"display_name": "API Endpoint", "advanced": True, "value": "https://api.mistral.ai/v1/"},
|
||||
}
|
||||
|
||||
def build(
|
||||
|
|
@ -51,7 +47,7 @@ class MistralAIEmbeddingsComponent(CustomComponent):
|
|||
max_concurrent_requests: int = 64,
|
||||
max_retries: int = 5,
|
||||
timeout: int = 120,
|
||||
endpoint: str = "https://api.mistral.ai/v1/"
|
||||
endpoint: str = "https://api.mistral.ai/v1/",
|
||||
) -> Embeddings:
|
||||
if mistral_api_key:
|
||||
api_key = SecretStr(mistral_api_key)
|
||||
|
|
@ -64,6 +60,5 @@ class MistralAIEmbeddingsComponent(CustomComponent):
|
|||
endpoint=endpoint,
|
||||
max_concurrent_requests=max_concurrent_requests,
|
||||
max_retries=max_retries,
|
||||
timeout=timeout
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain_community.embeddings import OllamaEmbeddings
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class OllamaEmbeddingsComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ from langflow.interface.custom.custom_component import CustomComponent
|
|||
from langflow.schema import Record
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class PassComponent(CustomComponent):
|
||||
display_name = "Pass"
|
||||
description = "A pass-through component that forwards the second input while ignoring the first, used for controlling workflow direction."
|
||||
field_order = ["ignored_input", "forwarded_input"]
|
||||
|
||||
|
||||
def build_config(self) -> dict:
|
||||
return {
|
||||
"ignored_input": {
|
||||
|
|
@ -19,7 +20,7 @@ class PassComponent(CustomComponent):
|
|||
"display_name": "Input",
|
||||
"info": "This input is forwarded by the component.",
|
||||
"input_types": ["Text", "Record"],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, ignored_input: Text, forwarded_input: Text) -> Union[Text, Record]:
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ class StoreMessageComponent(CustomComponent):
|
|||
session_id: Optional[str] = None,
|
||||
message: str = "",
|
||||
) -> List[Record]:
|
||||
|
||||
store_message(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class SubFlowComponent(CustomComponent):
|
|||
build_config["flow_name"]["options"] = self.get_flow_names()
|
||||
# Clean up the build config
|
||||
for key in list(build_config.keys()):
|
||||
if key not in self.field_order + ["code", "_type"]:
|
||||
if key not in self.field_order + ["code", "_type", "get_final_results_only"]:
|
||||
del build_config[key]
|
||||
if field_value is not None and field_name == "flow_name":
|
||||
try:
|
||||
|
|
@ -85,20 +85,29 @@ class SubFlowComponent(CustomComponent):
|
|||
"display_name": "Tweaks",
|
||||
"info": "Tweaks to apply to the flow.",
|
||||
},
|
||||
"get_final_results_only": {
|
||||
"display_name": "Get Final Results Only",
|
||||
"info": "If False, the output will contain all outputs from the flow.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build_records_from_result_data(self, result_data: ResultData) -> List[Record]:
|
||||
def build_records_from_result_data(self, result_data: ResultData, get_final_results_only: bool) -> List[Record]:
|
||||
messages = result_data.messages
|
||||
if not messages:
|
||||
return []
|
||||
records = []
|
||||
for message in messages:
|
||||
message_dict = message if isinstance(message, dict) else message.model_dump()
|
||||
record = Record(data={"result": result_data.model_dump(), "message": message_dict.get("message", "")})
|
||||
if get_final_results_only:
|
||||
result_data_dict = result_data.model_dump()
|
||||
results = result_data_dict.get("results", {})
|
||||
inner_result = results.get("result", {})
|
||||
record = Record(data={"result": inner_result, "message": message_dict}, text_key="result")
|
||||
records.append(record)
|
||||
return records
|
||||
|
||||
async def build(self, flow_name: str, **kwargs) -> List[Record]:
|
||||
async def build(self, flow_name: str, get_final_results_only: bool = True, **kwargs) -> List[Record]:
|
||||
tweaks = {key: {"input_value": value} for key, value in kwargs.items()}
|
||||
run_outputs: List[Optional[RunOutputs]] = await self.run_flow(
|
||||
tweaks=tweaks,
|
||||
|
|
@ -112,7 +121,7 @@ class SubFlowComponent(CustomComponent):
|
|||
if run_output is not None:
|
||||
for output in run_output.outputs:
|
||||
if output:
|
||||
records.extend(self.build_records_from_result_data(output))
|
||||
records.extend(self.build_records_from_result_data(output, get_final_results_only))
|
||||
|
||||
self.status = records
|
||||
logger.debug(records)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from langflow.interface.custom.custom_component import CustomComponent
|
|||
from langflow.schema import Record
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class TextOperatorComponent(CustomComponent):
|
||||
display_name = "Text Operator"
|
||||
description = "Compares two text inputs based on a specified condition such as equality or inequality, with optional case sensitivity."
|
||||
|
|
@ -21,14 +22,7 @@ class TextOperatorComponent(CustomComponent):
|
|||
"operator": {
|
||||
"display_name": "Operator",
|
||||
"info": "The operator to apply for comparing the texts.",
|
||||
"options": [
|
||||
"equals",
|
||||
"not equals",
|
||||
"contains",
|
||||
"starts with",
|
||||
"ends with",
|
||||
"exists"
|
||||
],
|
||||
"options": ["equals", "not equals", "contains", "starts with", "ends with", "exists"],
|
||||
},
|
||||
"case_sensitive": {
|
||||
"display_name": "Case Sensitive",
|
||||
|
|
@ -51,11 +45,8 @@ class TextOperatorComponent(CustomComponent):
|
|||
case_sensitive: bool = False,
|
||||
true_output: Optional[Text] = "",
|
||||
) -> Union[Text, Record]:
|
||||
|
||||
if not input_text or not match_text:
|
||||
raise ValueError(
|
||||
"Both 'input_text' and 'match_text' must be provided and non-empty."
|
||||
)
|
||||
raise ValueError("Both 'input_text' and 'match_text' must be provided and non-empty.")
|
||||
|
||||
if not case_sensitive:
|
||||
input_text = input_text.lower()
|
||||
|
|
@ -82,4 +73,4 @@ class TextOperatorComponent(CustomComponent):
|
|||
self.status = "Comparison failed, stopping execution."
|
||||
self.stop()
|
||||
|
||||
return output_record
|
||||
return output_record
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
from typing import Optional, cast
|
||||
|
||||
from langchain_astradb.chat_message_histories import AstraDBChatMessageHistory
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class AstraDBMessageReaderComponent(BaseMemoryComponent):
|
||||
display_name = "Astra DB Message Reader"
|
||||
description = "Retrieves stored chat messages from Astra DB."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"collection_name": {
|
||||
"display_name": "Collection Name",
|
||||
"info": "Collection name for Astra DB.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"token": {
|
||||
"display_name": "Astra DB Application Token",
|
||||
"info": "Token for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"api_endpoint": {
|
||||
"display_name": "Astra DB API Endpoint",
|
||||
"info": "API Endpoint for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"namespace": {
|
||||
"display_name": "Namespace",
|
||||
"info": "Namespace for the Astra DB instance.",
|
||||
"input_types": ["Text"],
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
"""
|
||||
Retrieves messages from the AstraDBChatMessageHistory memory.
|
||||
|
||||
Args:
|
||||
memory (AstraDBChatMessageHistory): The AstraDBChatMessageHistory instance to retrieve messages from.
|
||||
|
||||
Returns:
|
||||
list[Record]: A list of Record objects representing the search results.
|
||||
"""
|
||||
memory: AstraDBChatMessageHistory = cast(AstraDBChatMessageHistory, kwargs.get("memory"))
|
||||
if not memory:
|
||||
raise ValueError("AstraDBChatMessageHistory instance is required.")
|
||||
|
||||
# Get messages from the memory
|
||||
messages = memory.messages
|
||||
results = [Record.from_lc_message(message) for message in messages]
|
||||
|
||||
return list(results)
|
||||
|
||||
def build(
|
||||
self,
|
||||
session_id: Text,
|
||||
collection_name: str,
|
||||
token: str,
|
||||
api_endpoint: str,
|
||||
namespace: Optional[str] = None,
|
||||
) -> list[Record]:
|
||||
try:
|
||||
pass
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import langchain Astra DB integration package. "
|
||||
"Please install it with `pip install langchain-astradb`."
|
||||
)
|
||||
|
||||
memory = AstraDBChatMessageHistory(
|
||||
session_id=session_id,
|
||||
collection_name=collection_name,
|
||||
token=token,
|
||||
api_endpoint=api_endpoint,
|
||||
namespace=namespace,
|
||||
)
|
||||
|
||||
records = self.get_messages(memory=memory)
|
||||
self.status = records
|
||||
|
||||
return records
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_astradb import AstraDBChatMessageHistory
|
||||
|
||||
|
||||
class AstraDBMessageWriterComponent(BaseMemoryComponent):
|
||||
display_name = "Astra DB Message Writer"
|
||||
description = "Writes a message to Astra DB."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {
|
||||
"display_name": "Input Record",
|
||||
"info": "Record to write to Astra DB.",
|
||||
},
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"collection_name": {
|
||||
"display_name": "Collection Name",
|
||||
"info": "Collection name for Astra DB.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"token": {
|
||||
"display_name": "Astra DB Application Token",
|
||||
"info": "Token for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"api_endpoint": {
|
||||
"display_name": "Astra DB API Endpoint",
|
||||
"info": "API Endpoint for the Astra DB instance.",
|
||||
"password": True,
|
||||
},
|
||||
"namespace": {
|
||||
"display_name": "Namespace",
|
||||
"info": "Namespace for the Astra DB instance.",
|
||||
"input_types": ["Text"],
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def add_message(
|
||||
self,
|
||||
sender: str,
|
||||
sender_name: str,
|
||||
text: Text,
|
||||
session_id: str,
|
||||
metadata: Optional[dict] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Adds a message to the AstraDBChatMessageHistory memory.
|
||||
|
||||
Args:
|
||||
sender (Text): The type of the message sender. Valid values are "Machine" or "User".
|
||||
sender_name (Text): The name of the message sender.
|
||||
text (Text): The content of the message.
|
||||
session_id (Text): The session ID associated with the message.
|
||||
metadata (dict | None, optional): Additional metadata for the message. Defaults to None.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Raises:
|
||||
ValueError: If the AstraDBChatMessageHistory instance is not provided.
|
||||
|
||||
"""
|
||||
memory: AstraDBChatMessageHistory | None = kwargs.pop("memory", None)
|
||||
if memory is None:
|
||||
raise ValueError("AstraDBChatMessageHistory instance is required.")
|
||||
|
||||
text_list = [
|
||||
BaseMessage(
|
||||
content=text,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
metadata=metadata,
|
||||
session_id=session_id,
|
||||
)
|
||||
]
|
||||
|
||||
memory.add_messages(text_list)
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_value: Record,
|
||||
session_id: Text,
|
||||
collection_name: str,
|
||||
token: str,
|
||||
api_endpoint: str,
|
||||
namespace: Optional[str] = None,
|
||||
) -> Record:
|
||||
try:
|
||||
pass
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import langchain Astra DB integration package. "
|
||||
"Please install it with `pip install langchain-astradb`."
|
||||
)
|
||||
|
||||
memory = AstraDBChatMessageHistory(
|
||||
session_id=session_id,
|
||||
collection_name=collection_name,
|
||||
token=token,
|
||||
api_endpoint=api_endpoint,
|
||||
namespace=namespace,
|
||||
)
|
||||
|
||||
self.add_message(**input_value.data, memory=memory)
|
||||
self.status = f"Added message to Astra DB memory for session {session_id}"
|
||||
|
||||
return input_value
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLanguageModel
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
|
||||
|
||||
class ChatAntropicSpecsComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLanguageModel
|
||||
from langchain_community.chat_models.azure_openai import AzureChatOpenAI
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
from langchain_openai import AzureChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
|
||||
class AzureChatOpenAISpecsComponent(CustomComponent):
|
||||
|
|
@ -84,13 +84,17 @@ class AzureChatOpenAISpecsComponent(CustomComponent):
|
|||
temperature: float = 0.7,
|
||||
max_tokens: Optional[int] = 1000,
|
||||
) -> BaseLanguageModel:
|
||||
if api_key:
|
||||
azure_api_key = SecretStr(api_key)
|
||||
else:
|
||||
azure_api_key = None
|
||||
try:
|
||||
llm = AzureChatOpenAI(
|
||||
model=model,
|
||||
azure_endpoint=azure_endpoint,
|
||||
azure_deployment=azure_deployment,
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
api_key=azure_api_key,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_community.chat_models.openai import ChatOpenAI
|
||||
from langchain_openai import ChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
|
||||
from langflow.base.models.openai_constants import MODEL_NAMES
|
||||
from langflow.field_typing import BaseLanguageModel, NestedDict
|
||||
|
|
@ -59,11 +61,15 @@ class ChatOpenAIComponent(CustomComponent):
|
|||
) -> BaseLanguageModel:
|
||||
if not openai_api_base:
|
||||
openai_api_base = "https://api.openai.com/v1"
|
||||
if openai_api_key:
|
||||
api_key = SecretStr(openai_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
return ChatOpenAI(
|
||||
max_tokens=max_tokens,
|
||||
model_kwargs=model_kwargs,
|
||||
model=model_name,
|
||||
base_url=openai_api_base,
|
||||
api_key=openai_api_key,
|
||||
api_key=api_key,
|
||||
temperature=temperature,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class RecordsOutput(CustomComponent):
|
||||
display_name = "Records Output"
|
||||
description = "Display Records as a Table"
|
||||
|
||||
def build(self, input_value: Record) -> Record:
|
||||
return input_value
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.retrievers import AmazonKendraRetriever
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
|
||||
class AmazonKendraRetrieverComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.retrievers import MetalRetriever
|
||||
from metal_sdk.metal import Metal # type: ignore
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
|
||||
class MetalRetrieverComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import json
|
||||
from typing import List
|
||||
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.chains.query_constructor.base import AttributeInfo
|
||||
from langchain.retrievers.self_query.base import SelfQueryRetriever
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain.schema.vectorstore import VectorStore
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class VectaraSelfQueryRetriverComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
from typing import List
|
||||
|
||||
from langchain.text_splitter import CharacterTextSplitter
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.utils.util import unescape_string
|
||||
from langchain_text_splitters import CharacterTextSplitter
|
||||
|
||||
|
||||
class CharacterTextSplitterComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain.text_splitter import Language
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_text_splitters import Language, RecursiveCharacterTextSplitter
|
||||
|
||||
|
||||
class LanguageRecursiveTextSplitterComponent(CustomComponent):
|
||||
|
|
@ -61,7 +60,6 @@ class LanguageRecursiveTextSplitterComponent(CustomComponent):
|
|||
Returns:
|
||||
list[str]: The chunks of text.
|
||||
"""
|
||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||
|
||||
# Make sure chunk_size and chunk_overlap are ints
|
||||
if isinstance(chunk_size, str):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.utils.util import build_loader_repr_from_records, unescape_string
|
||||
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
||||
|
||||
|
||||
class RecursiveCharacterTextSplitterComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
from typing import List, Union
|
||||
|
||||
from langchain.agents import tool
|
||||
from langchain.agents.agent_toolkits.base import BaseToolkit
|
||||
from langchain.tools import Tool
|
||||
from metaphor_python import Metaphor # type: ignore
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_community.agent_toolkits.base import BaseToolkit
|
||||
from langchain_core.tools import Tool, tool
|
||||
|
||||
|
||||
class MetaphorToolkit(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from langchain.agents.agent_toolkits.vectorstore.toolkit import VectorStoreInfo
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class VectorStoreInfoComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import importlib
|
||||
|
||||
from langchain.agents import Tool
|
||||
from langchain_experimental.utilities import PythonREPL
|
||||
|
||||
from langflow.base.tools.base import build_status_from_tool
|
||||
from langflow.custom import CustomComponent
|
||||
from langchain_core.tools import Tool
|
||||
|
||||
|
||||
class PythonREPLToolComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
from typing import List
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Couchbase import CouchbaseComponent
|
||||
from langflow.field_typing import Embeddings, Text
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class CouchbaseSearchComponent(LCVectorStoreComponent):
|
||||
display_name = "Couchbase Search"
|
||||
description = "Search a Couchbase Vector Store for similar documents."
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/couchbase"
|
||||
icon = "Couchbase"
|
||||
field_order = [
|
||||
"couchbase_connection_string",
|
||||
"couchbase_username",
|
||||
"couchbase_password",
|
||||
"bucket_name",
|
||||
"scope_name",
|
||||
"collection_name",
|
||||
"index_name",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {"display_name": "Input"},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"couchbase_connection_string": {"display_name": "Couchbase Cluster connection string", "required": True},
|
||||
"couchbase_username": {"display_name": "Couchbase username", "required": True},
|
||||
"couchbase_password": {"display_name": "Couchbase password", "password": True, "required": True},
|
||||
"bucket_name": {"display_name": "Bucket Name", "required": True},
|
||||
"scope_name": {"display_name": "Scope Name", "required": True},
|
||||
"collection_name": {"display_name": "Collection Name", "required": True},
|
||||
"index_name": {"display_name": "Index Name", "required": True},
|
||||
"number_of_results": {
|
||||
"display_name": "Number of Results",
|
||||
"info": "Number of results to return.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build( # type: ignore[override]
|
||||
self,
|
||||
input_value: Text,
|
||||
embedding: Embeddings,
|
||||
number_of_results: int = 4,
|
||||
bucket_name: str = "",
|
||||
scope_name: str = "",
|
||||
collection_name: str = "",
|
||||
index_name: str = "",
|
||||
couchbase_connection_string: str = "",
|
||||
couchbase_username: str = "",
|
||||
couchbase_password: str = "",
|
||||
) -> List[Record]:
|
||||
vector_store = CouchbaseComponent().build(
|
||||
couchbase_connection_string=couchbase_connection_string,
|
||||
couchbase_username=couchbase_username,
|
||||
couchbase_password=couchbase_password,
|
||||
bucket_name=bucket_name,
|
||||
scope_name=scope_name,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
)
|
||||
if not vector_store:
|
||||
raise ValueError("Failed to create Couchbase Vector Store")
|
||||
return self.search_with_vector_store(
|
||||
vector_store=vector_store, input_value=input_value, search_type="similarity", k=number_of_results
|
||||
)
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Redis import RedisComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class RedisSearchComponent(RedisComponent, LCVectorStoreComponent):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Weaviate import WeaviateVectorStoreComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreComponent):
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ from .SupabaseVectorStoreSearch import SupabaseSearchComponent
|
|||
from .VectaraSearch import VectaraSearchComponent
|
||||
from .WeaviateSearch import WeaviateSearchVectorStore
|
||||
from .pgvectorSearch import PGVectorSearchComponent
|
||||
from .Couchbase import CouchbaseSearchComponent # type: ignore
|
||||
|
||||
__all__ = [
|
||||
"AstraDBSearchComponent",
|
||||
"ChromaSearchComponent",
|
||||
"CouchbaseSearchComponent",
|
||||
"FAISSSearchComponent",
|
||||
"MongoDBAtlasSearchComponent",
|
||||
"PineconeSearchComponent",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from typing import List
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.pgvector import PGVectorComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
|
||||
|
||||
class PGVectorSearchComponent(PGVectorComponent, LCVectorStoreComponent):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_astradb import AstraDBVectorStore
|
||||
from langchain_astradb.utils.astradb import SetupMode
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, VectorStore
|
||||
from langflow.schema import Record
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
|
||||
class AstraDBVectorStoreComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
import chromadb # type: ignore
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.chroma import Chroma
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class ChromaComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain_community.vectorstores import CouchbaseVectorStore
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Embeddings, VectorStore
|
||||
from langflow.schema import Record
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from couchbase.auth import PasswordAuthenticator # type: ignore
|
||||
from couchbase.cluster import Cluster # type: ignore
|
||||
from couchbase.options import ClusterOptions # type: ignore
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
|
||||
class CouchbaseComponent(CustomComponent):
|
||||
display_name = "Couchbase"
|
||||
description = "Construct a `Couchbase Vector Search` vector store from raw documents."
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/couchbase"
|
||||
icon = "Couchbase"
|
||||
field_order = [
|
||||
"couchbase_connection_string",
|
||||
"couchbase_username",
|
||||
"couchbase_password",
|
||||
"bucket_name",
|
||||
"scope_name",
|
||||
"collection_name",
|
||||
"index_name",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"inputs": {"display_name": "Input", "input_types": ["Document", "Record"]},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"couchbase_connection_string": {"display_name": "Couchbase Cluster connection string", "required": True},
|
||||
"couchbase_username": {"display_name": "Couchbase username", "required": True},
|
||||
"couchbase_password": {"display_name": "Couchbase password", "password": True, "required": True},
|
||||
"bucket_name": {"display_name": "Bucket Name", "required": True},
|
||||
"scope_name": {"display_name": "Scope Name", "required": True},
|
||||
"collection_name": {"display_name": "Collection Name", "required": True},
|
||||
"index_name": {"display_name": "Index Name", "required": True},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
embedding: Embeddings,
|
||||
inputs: Optional[List[Record]] = None,
|
||||
bucket_name: str = "",
|
||||
scope_name: str = "",
|
||||
collection_name: str = "",
|
||||
index_name: str = "",
|
||||
couchbase_connection_string: str = "",
|
||||
couchbase_username: str = "",
|
||||
couchbase_password: str = "",
|
||||
) -> Union[VectorStore, BaseRetriever]:
|
||||
try:
|
||||
auth = PasswordAuthenticator(couchbase_username, couchbase_password)
|
||||
options = ClusterOptions(auth)
|
||||
cluster = Cluster(couchbase_connection_string, options)
|
||||
|
||||
cluster.wait_until_ready(timedelta(seconds=5))
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to connect to Couchbase: {e}")
|
||||
documents = []
|
||||
for _input in inputs or []:
|
||||
if isinstance(_input, Record):
|
||||
documents.append(_input.to_lc_document())
|
||||
else:
|
||||
documents.append(_input)
|
||||
if documents:
|
||||
vector_store = CouchbaseVectorStore.from_documents(
|
||||
documents=documents,
|
||||
cluster=cluster,
|
||||
bucket_name=bucket_name,
|
||||
scope_name=scope_name,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
)
|
||||
else:
|
||||
vector_store = CouchbaseVectorStore(
|
||||
cluster=cluster,
|
||||
bucket_name=bucket_name,
|
||||
scope_name=scope_name,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
)
|
||||
return vector_store
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
from typing import List, Text, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.faiss import FAISS
|
||||
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class FAISSComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_core.documents import Document
|
||||
from langchain_pinecone._utilities import DistanceStrategy
|
||||
from langchain_pinecone.vectorstores import PineconeVectorStore
|
||||
|
|
@ -9,6 +6,8 @@ from langchain_pinecone.vectorstores import PineconeVectorStore
|
|||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class PineconeComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.qdrant import Qdrant
|
||||
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class QdrantComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.redis import Redis
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class RedisComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.supabase import SupabaseVectorStore
|
||||
from supabase.client import Client, create_client
|
||||
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class SupabaseComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
import weaviate # type: ignore
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore, Weaviate
|
||||
from langchain_community.vectorstores import Weaviate
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class WeaviateVectorStoreComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ from .SupabaseVectorStore import SupabaseComponent
|
|||
from .Vectara import VectaraComponent
|
||||
from .Weaviate import WeaviateVectorStoreComponent
|
||||
from .pgvector import PGVectorComponent
|
||||
from .Couchbase import CouchbaseComponent
|
||||
|
||||
__all__ = [
|
||||
"AstraDBVectorStoreComponent",
|
||||
"ChromaComponent",
|
||||
"CouchbaseComponent",
|
||||
"FAISSComponent",
|
||||
"MongoDBAtlasComponent",
|
||||
"PineconeComponent",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
from langchain.embeddings.base import Embeddings
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.pgvector import PGVector
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
|
||||
|
||||
class PGVectorComponent(CustomComponent):
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@ from typing import Callable, Dict, Text, Union
|
|||
|
||||
from langchain.agents.agent import AgentExecutor
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.document_loaders.base import BaseLoader
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.memory.chat_memory import BaseChatMemory
|
||||
from langchain.prompts import BasePromptTemplate, ChatPromptTemplate, PromptTemplate
|
||||
from langchain.schema import BaseOutputParser, BaseRetriever, Document
|
||||
from langchain.schema.embeddings import Embeddings
|
||||
from langchain.schema.language_model import BaseLanguageModel
|
||||
from langchain.schema.memory import BaseMemory
|
||||
from langchain.text_splitter import TextSplitter
|
||||
from langchain.tools import Tool
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_core.document_loaders import BaseLoader
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.language_models import BaseLLM, BaseLanguageModel
|
||||
from langchain_core.memory import BaseMemory
|
||||
from langchain_core.output_parsers import BaseOutputParser
|
||||
from langchain_core.prompts import BasePromptTemplate, ChatPromptTemplate, PromptTemplate
|
||||
from langchain_core.retrievers import BaseRetriever
|
||||
from langchain_core.tools import Tool
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
from langchain_text_splitters import TextSplitter
|
||||
|
||||
# Type alias for more complex dicts
|
||||
NestedDict = Dict[str, Union[str, Dict]]
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ from typing import TYPE_CHECKING, Any, List, Optional
|
|||
from loguru import logger
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from langflow.graph.edge.utils import build_clean_params
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.services.monitor.utils import log_message
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -143,7 +141,6 @@ class ContractEdge(Edge):
|
|||
if not self.is_fulfilled:
|
||||
await self.honor(source, target)
|
||||
|
||||
log_transaction(self, source, target, "success")
|
||||
# If the target vertex is a power component we log messages
|
||||
if target.vertex_type == "ChatOutput" and (
|
||||
isinstance(target.params.get(INPUT_FIELD_NAME), str)
|
||||
|
|
@ -157,26 +154,9 @@ class ContractEdge(Edge):
|
|||
message=target.params.get(INPUT_FIELD_NAME, {}),
|
||||
session_id=target.params.get("session_id", ""),
|
||||
artifacts=target.artifacts,
|
||||
flow_id=target.graph.flow_id,
|
||||
)
|
||||
return self.result
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.source_id} -[{self.target_param}]-> {self.target_id}"
|
||||
|
||||
|
||||
def log_transaction(edge: ContractEdge, source: "Vertex", target: "Vertex", status, error=None):
|
||||
try:
|
||||
monitor_service = get_monitor_service()
|
||||
clean_params = build_clean_params(target)
|
||||
data = {
|
||||
"source": source.vertex_type,
|
||||
"target": target.vertex_type,
|
||||
"target_args": clean_params,
|
||||
"timestamp": monitor_service.get_timestamp(),
|
||||
"status": status,
|
||||
"error": error,
|
||||
}
|
||||
monitor_service.add_row(table_name="transactions", data=data)
|
||||
except Exception as e:
|
||||
logger.error(f"Error logging transaction: {e}")
|
||||
logger.error(f"Error logging transaction: {e}")
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
|
||||
|
||||
def build_clean_params(target: "Vertex") -> dict:
|
||||
"""
|
||||
Cleans the parameters of the target vertex.
|
||||
"""
|
||||
# Removes all keys that the values aren't python types like str, int, bool, etc.
|
||||
params = {
|
||||
key: value for key, value in target.params.items() if isinstance(value, (str, int, bool, float, list, dict))
|
||||
}
|
||||
# if it is a list we need to check if the contents are python types
|
||||
for key, value in params.items():
|
||||
if isinstance(value, list):
|
||||
params[key] = [item for item in value if isinstance(item, (str, int, bool, float, list, dict))]
|
||||
return params
|
||||
|
|
@ -14,7 +14,7 @@ from langflow.graph.graph.state_manager import GraphStateManager
|
|||
from langflow.graph.graph.utils import process_flow
|
||||
from langflow.graph.schema import InterfaceComponentTypes, RunOutputs
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.graph.vertex.types import ChatVertex, FileToolVertex, LLMVertex, StateVertex, ToolkitVertex
|
||||
from langflow.graph.vertex.types import FileToolVertex, InterfaceVertex, LLMVertex, StateVertex, ToolkitVertex
|
||||
from langflow.interface.tools.constants import FILE_TOOLS
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME, InputType
|
||||
|
|
@ -242,6 +242,7 @@ class Graph:
|
|||
outputs: list[str],
|
||||
stream: bool,
|
||||
session_id: str,
|
||||
fallback_to_env_vars: bool,
|
||||
) -> List[Optional["ResultData"]]:
|
||||
"""
|
||||
Runs the graph with the given inputs.
|
||||
|
|
@ -289,7 +290,7 @@ class Graph:
|
|||
start_component_id = next(
|
||||
(vertex_id for vertex_id in self._is_input_vertices if "chat" in vertex_id.lower()), None
|
||||
)
|
||||
await self.process(start_component_id=start_component_id)
|
||||
await self.process(start_component_id=start_component_id, fallback_to_env_vars=fallback_to_env_vars)
|
||||
self.increment_run_count()
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
|
|
@ -315,6 +316,7 @@ class Graph:
|
|||
outputs: Optional[list[str]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
fallback_to_env_vars: bool = False,
|
||||
) -> List[RunOutputs]:
|
||||
"""
|
||||
Run the graph with the given inputs and return the outputs.
|
||||
|
|
@ -340,6 +342,7 @@ class Graph:
|
|||
outputs=outputs,
|
||||
session_id=session_id,
|
||||
stream=stream,
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
@ -362,6 +365,7 @@ class Graph:
|
|||
outputs: Optional[list[str]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
fallback_to_env_vars: bool = False,
|
||||
) -> List[RunOutputs]:
|
||||
"""
|
||||
Runs the graph with the given inputs.
|
||||
|
|
@ -403,6 +407,7 @@ class Graph:
|
|||
outputs=outputs or [],
|
||||
stream=stream,
|
||||
session_id=session_id or "",
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
run_output_object = RunOutputs(inputs=run_inputs, outputs=run_outputs)
|
||||
logger.debug(f"Run outputs: {run_output_object}")
|
||||
|
|
@ -468,9 +473,9 @@ class Graph:
|
|||
"""Marks a branch of the graph."""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
visited.add(vertex_id)
|
||||
if vertex_id in visited:
|
||||
return
|
||||
visited.add(vertex_id)
|
||||
|
||||
self.mark_vertex(vertex_id, state)
|
||||
|
||||
|
|
@ -712,6 +717,7 @@ class Graph:
|
|||
vertex_id: str,
|
||||
inputs_dict: Optional[Dict[str, str]] = None,
|
||||
user_id: Optional[str] = None,
|
||||
fallback_to_env_vars: bool = False,
|
||||
):
|
||||
"""
|
||||
Builds a vertex in the graph.
|
||||
|
|
@ -733,7 +739,7 @@ class Graph:
|
|||
vertex = self.get_vertex(vertex_id)
|
||||
try:
|
||||
if not vertex.frozen or not vertex._built:
|
||||
await vertex.build(user_id=user_id, inputs=inputs_dict)
|
||||
await vertex.build(user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars)
|
||||
|
||||
if vertex.result is not None:
|
||||
params = vertex._built_object_repr()
|
||||
|
|
@ -796,7 +802,7 @@ class Graph:
|
|||
vertices.append(vertex)
|
||||
return vertices
|
||||
|
||||
async def process(self, start_component_id: Optional[str] = None) -> "Graph":
|
||||
async def process(self, fallback_to_env_vars: bool, start_component_id: Optional[str] = None) -> "Graph":
|
||||
"""Processes the graph with vertices in each layer run in parallel."""
|
||||
|
||||
first_layer = self.sort_vertices(start_component_id=start_component_id)
|
||||
|
|
@ -821,6 +827,7 @@ class Graph:
|
|||
vertex_id=vertex_id,
|
||||
user_id=self.user_id,
|
||||
inputs_dict={},
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
),
|
||||
name=f"{vertex.display_name} Run {vertex_task_run_count.get(vertex_id, 0)}",
|
||||
)
|
||||
|
|
@ -987,8 +994,8 @@ class Graph:
|
|||
"""Returns the node class based on the node type."""
|
||||
# First we check for the node_base_type
|
||||
node_name = node_id.split("-")[0]
|
||||
if node_name in ["ChatOutput", "ChatInput"]:
|
||||
return ChatVertex
|
||||
if node_name in InterfaceComponentTypes:
|
||||
return InterfaceVertex
|
||||
elif node_name in ["SharedState", "Notify", "Listen"]:
|
||||
return StateVertex
|
||||
elif node_base_type in lazy_load_vertex_dict.VERTEX_TYPE_MAP:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from langflow.graph.schema import CHAT_COMPONENTS
|
||||
from langflow.graph.vertex import types
|
||||
from langflow.interface.agents.base import agent_creator
|
||||
from langflow.interface.custom.base import custom_component_creator
|
||||
|
|
@ -13,8 +14,6 @@ from langflow.interface.tools.base import tool_creator
|
|||
from langflow.interface.wrappers.base import wrapper_creator
|
||||
from langflow.utils.lazy_load import LazyLoadDictBase
|
||||
|
||||
CHAT_COMPONENTS = ["ChatInput", "ChatOutput", "TextInput", "SessionID"]
|
||||
|
||||
|
||||
class VertexTypesDict(LazyLoadDictBase):
|
||||
def __init__(self):
|
||||
|
|
@ -47,7 +46,7 @@ class VertexTypesDict(LazyLoadDictBase):
|
|||
**{t: types.TextSplitterVertex for t in textsplitter_creator.to_list()},
|
||||
**{t: types.CustomComponentVertex for t in custom_component_creator.to_list()},
|
||||
**{t: types.RetrieverVertex for t in retriever_creator.to_list()},
|
||||
**{t: types.ChatVertex for t in CHAT_COMPONENTS},
|
||||
**{t: types.InterfaceVertex for t in CHAT_COMPONENTS},
|
||||
}
|
||||
|
||||
def get_custom_component_vertex_type(self):
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class InterfaceComponentTypes(str, Enum, metaclass=ContainsEnumMeta):
|
|||
ChatOutput = "ChatOutput"
|
||||
TextInput = "TextInput"
|
||||
TextOutput = "TextOutput"
|
||||
RecordsOutput = "RecordsOutput"
|
||||
|
||||
def __contains__(cls, item):
|
||||
try:
|
||||
|
|
@ -40,6 +41,8 @@ class InterfaceComponentTypes(str, Enum, metaclass=ContainsEnumMeta):
|
|||
return True
|
||||
|
||||
|
||||
CHAT_COMPONENTS = [InterfaceComponentTypes.ChatInput, InterfaceComponentTypes.ChatOutput]
|
||||
RECORDS_COMPONENTS = [InterfaceComponentTypes.RecordsOutput]
|
||||
INPUT_COMPONENTS = [
|
||||
InterfaceComponentTypes.ChatInput,
|
||||
InterfaceComponentTypes.TextInput,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from loguru import logger
|
|||
|
||||
from langflow.graph.schema import INPUT_COMPONENTS, OUTPUT_COMPONENTS, InterfaceComponentTypes, ResultData
|
||||
from langflow.graph.utils import UnbuiltObject, UnbuiltResult
|
||||
from langflow.graph.vertex.utils import generate_result
|
||||
from langflow.graph.vertex.utils import generate_result, log_transaction
|
||||
from langflow.interface.initialize import loading
|
||||
from langflow.interface.listing import lazy_load_dict
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
|
|
@ -317,7 +317,11 @@ class Vertex:
|
|||
params[field_name] = full_path
|
||||
elif field.get("required"):
|
||||
field_display_name = field.get("display_name")
|
||||
raise ValueError(f"File path not found for {field_display_name} in component {self.display_name}")
|
||||
logger.warning(
|
||||
f"File path not found for {field_display_name} in component {self.display_name}. Setting to None."
|
||||
)
|
||||
params[field_name] = None
|
||||
|
||||
elif field.get("type") in DIRECT_TYPES and params.get(field_name) is None:
|
||||
val = field.get("value")
|
||||
if field.get("type") == "code":
|
||||
|
|
@ -392,13 +396,17 @@ class Vertex:
|
|||
self.params = self._raw_params.copy()
|
||||
self.updated_raw_params = True
|
||||
|
||||
async def _build(self, user_id=None):
|
||||
async def _build(
|
||||
self,
|
||||
fallback_to_env_vars,
|
||||
user_id=None,
|
||||
):
|
||||
"""
|
||||
Initiate the build process.
|
||||
"""
|
||||
logger.debug(f"Building {self.display_name}")
|
||||
await self._build_each_vertex_in_params_dict(user_id)
|
||||
await self._get_and_instantiate_class(user_id)
|
||||
await self._get_and_instantiate_class(user_id, fallback_to_env_vars)
|
||||
self._validate_built_object()
|
||||
|
||||
self._built = True
|
||||
|
|
@ -434,7 +442,11 @@ class Vertex:
|
|||
# to the frontend
|
||||
self.set_artifacts()
|
||||
artifacts = self.artifacts
|
||||
messages = self.extract_messages_from_artifacts(artifacts)
|
||||
if isinstance(artifacts, dict):
|
||||
messages = self.extract_messages_from_artifacts(artifacts)
|
||||
else:
|
||||
messages = []
|
||||
|
||||
result_dict = ResultData(
|
||||
results=result_dict,
|
||||
artifacts=artifacts,
|
||||
|
|
@ -502,7 +514,7 @@ class Vertex:
|
|||
if not self._is_vertex(value):
|
||||
self.params[key][sub_key] = value
|
||||
else:
|
||||
result = await value.get_result()
|
||||
result = await value.get_result(self)
|
||||
self.params[key][sub_key] = result
|
||||
|
||||
def _is_vertex(self, value):
|
||||
|
|
@ -517,9 +529,7 @@ class Vertex:
|
|||
"""
|
||||
return all(self._is_vertex(vertex) for vertex in value)
|
||||
|
||||
async def get_result(
|
||||
self,
|
||||
) -> Any:
|
||||
async def get_result(self, requester: "Vertex") -> Any:
|
||||
"""
|
||||
Retrieves the result of the vertex.
|
||||
|
||||
|
|
@ -529,9 +539,9 @@ class Vertex:
|
|||
The result of the vertex.
|
||||
"""
|
||||
async with self._lock:
|
||||
return await self._get_result()
|
||||
return await self._get_result(requester)
|
||||
|
||||
async def _get_result(self) -> Any:
|
||||
async def _get_result(self, requester: "Vertex") -> Any:
|
||||
"""
|
||||
Retrieves the result of the built component.
|
||||
|
||||
|
|
@ -541,15 +551,19 @@ class Vertex:
|
|||
The built result if use_result is True, else the built object.
|
||||
"""
|
||||
if not self._built:
|
||||
log_transaction(source=self, target=requester, flow_id=self.graph.flow_id, status="error")
|
||||
raise ValueError(f"Component {self.display_name} has not been built yet")
|
||||
return self._built_result if self.use_result else self._built_object
|
||||
|
||||
result = self._built_result if self.use_result else self._built_object
|
||||
log_transaction(source=self, target=requester, flow_id=self.graph.flow_id, status="success")
|
||||
return result
|
||||
|
||||
async def _build_vertex_and_update_params(self, key, vertex: "Vertex"):
|
||||
"""
|
||||
Builds a given vertex and updates the params dictionary accordingly.
|
||||
"""
|
||||
|
||||
result = await vertex.get_result()
|
||||
result = await vertex.get_result(self)
|
||||
self._handle_func(key, result)
|
||||
if isinstance(result, list):
|
||||
self._extend_params_list_with_result(key, result)
|
||||
|
|
@ -565,7 +579,7 @@ class Vertex:
|
|||
"""
|
||||
self.params[key] = []
|
||||
for vertex in vertices:
|
||||
result = await vertex.get_result()
|
||||
result = await vertex.get_result(self)
|
||||
# Weird check to see if the params[key] is a list
|
||||
# because sometimes it is a Record and breaks the code
|
||||
if not isinstance(self.params[key], list):
|
||||
|
|
@ -608,7 +622,7 @@ class Vertex:
|
|||
if isinstance(self.params[key], list):
|
||||
self.params[key].extend(result)
|
||||
|
||||
async def _get_and_instantiate_class(self, user_id=None):
|
||||
async def _get_and_instantiate_class(self, user_id=None, fallback_to_env_vars=False):
|
||||
"""
|
||||
Gets the class from a dictionary and instantiates it with the params.
|
||||
"""
|
||||
|
|
@ -617,6 +631,7 @@ class Vertex:
|
|||
try:
|
||||
result = await loading.instantiate_class(
|
||||
user_id=user_id,
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
vertex=self,
|
||||
)
|
||||
self._update_built_object_and_artifacts(result)
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ import yaml
|
|||
from langchain_core.messages import AIMessage
|
||||
from loguru import logger
|
||||
|
||||
from langflow.graph.schema import InterfaceComponentTypes
|
||||
from langflow.graph.schema import CHAT_COMPONENTS, RECORDS_COMPONENTS, InterfaceComponentTypes
|
||||
from langflow.graph.utils import UnbuiltObject, flatten_list, serialize_field
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langflow.schema import Record
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.monitor.utils import log_vertex_build
|
||||
from langflow.utils.schemas import ChatOutputResponse
|
||||
from langflow.utils.schemas import ChatOutputResponse, RecordOutputResponse
|
||||
from langflow.utils.util import unescape_string
|
||||
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ class CustomComponentVertex(Vertex):
|
|||
return self.artifacts["repr"] or super()._built_object_repr()
|
||||
|
||||
|
||||
class ChatVertex(Vertex):
|
||||
class InterfaceVertex(Vertex):
|
||||
def __init__(self, data: Dict, graph):
|
||||
super().__init__(data, graph=graph, base_type="custom_components", is_task=True)
|
||||
self.steps = [self._build, self._run]
|
||||
|
|
@ -325,56 +325,131 @@ class ChatVertex(Vertex):
|
|||
return f"Task {self.task_id} is not running"
|
||||
if self.artifacts:
|
||||
# dump as a yaml string
|
||||
artifacts = {k.title().replace("_", " "): v for k, v in self.artifacts.items() if v is not None}
|
||||
if isinstance(self.artifacts, dict):
|
||||
_artifacts = [self.artifacts]
|
||||
elif hasattr(self.artifacts, "records"):
|
||||
_artifacts = self.artifacts.records
|
||||
else:
|
||||
_artifacts = self.artifacts
|
||||
artifacts = []
|
||||
for artifact in _artifacts:
|
||||
# artifacts = {k.title().replace("_", " "): v for k, v in self.artifacts.items() if v is not None}
|
||||
artifact = {k.title().replace("_", " "): v for k, v in artifact.items() if v is not None}
|
||||
artifacts.append(artifact)
|
||||
yaml_str = yaml.dump(artifacts, default_flow_style=False, allow_unicode=True)
|
||||
return yaml_str
|
||||
return super()._built_object_repr()
|
||||
|
||||
def _process_chat_component(self):
|
||||
"""
|
||||
Process the chat component and return the message.
|
||||
|
||||
This method processes the chat component by extracting the necessary parameters
|
||||
such as sender, sender_name, and message from the `params` dictionary. It then
|
||||
performs additional operations based on the type of the `_built_object` attribute.
|
||||
If `_built_object` is an instance of `AIMessage`, it creates a `ChatOutputResponse`
|
||||
object using the `from_message` method. If `_built_object` is not an instance of
|
||||
`UnbuiltObject`, it checks the type of `_built_object` and performs specific
|
||||
operations accordingly. If `_built_object` is a dictionary, it converts it into a
|
||||
code block. If `_built_object` is an instance of `Record`, it assigns the `text`
|
||||
attribute to the `message` variable. If `message` is an instance of `AsyncIterator`
|
||||
or `Iterator`, it builds a stream URL and sets `message` to an empty string. If
|
||||
`_built_object` is not a string, it converts it to a string. If `message` is a
|
||||
generator or iterator, it assigns it to the `message` variable. Finally, it creates
|
||||
a `ChatOutputResponse` object using the extracted parameters and assigns it to the
|
||||
`artifacts` attribute. If `artifacts` is not None, it calls the `model_dump` method
|
||||
on it and assigns the result to the `artifacts` attribute. It then returns the
|
||||
`message` variable.
|
||||
|
||||
Returns:
|
||||
str: The processed message.
|
||||
"""
|
||||
artifacts = None
|
||||
sender = self.params.get("sender", None)
|
||||
sender_name = self.params.get("sender_name", None)
|
||||
message = self.params.get(INPUT_FIELD_NAME, None)
|
||||
if isinstance(message, str):
|
||||
message = unescape_string(message)
|
||||
stream_url = None
|
||||
if isinstance(self._built_object, AIMessage):
|
||||
artifacts = ChatOutputResponse.from_message(
|
||||
self._built_object,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
)
|
||||
elif not isinstance(self._built_object, UnbuiltObject):
|
||||
if isinstance(self._built_object, dict):
|
||||
# Turn the dict into a pleasing to
|
||||
# read JSON inside a code block
|
||||
message = dict_to_codeblock(self._built_object)
|
||||
elif isinstance(self._built_object, Record):
|
||||
message = self._built_object.text
|
||||
elif isinstance(message, (AsyncIterator, Iterator)):
|
||||
stream_url = self.build_stream_url()
|
||||
message = ""
|
||||
elif not isinstance(self._built_object, str):
|
||||
message = str(self._built_object)
|
||||
# if the message is a generator or iterator
|
||||
# it means that it is a stream of messages
|
||||
else:
|
||||
message = self._built_object
|
||||
|
||||
artifacts = ChatOutputResponse(
|
||||
message=message,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
stream_url=stream_url,
|
||||
)
|
||||
|
||||
self.will_stream = stream_url is not None
|
||||
if artifacts:
|
||||
self.artifacts = artifacts.model_dump(exclude_none=True)
|
||||
|
||||
return message
|
||||
|
||||
def _process_record_component(self):
|
||||
"""
|
||||
Process the record component of the vertex.
|
||||
|
||||
If the built object is an instance of `Record`, it calls the `model_dump` method
|
||||
and assigns the result to the `artifacts` attribute.
|
||||
|
||||
If the built object is a list, it iterates over each element and checks if it is
|
||||
an instance of `Record`. If it is, it calls the `model_dump` method and appends
|
||||
the result to the `artifacts` list. If it is not, it raises a `ValueError` if the
|
||||
`ignore_errors` parameter is set to `False`, or logs an error message if it is set
|
||||
to `True`.
|
||||
|
||||
Returns:
|
||||
The built object.
|
||||
|
||||
Raises:
|
||||
ValueError: If an element in the list is not an instance of `Record` and
|
||||
`ignore_errors` is set to `False`.
|
||||
"""
|
||||
if isinstance(self._built_object, Record):
|
||||
artifacts = [self._built_object.data]
|
||||
elif isinstance(self._built_object, list):
|
||||
artifacts = []
|
||||
ignore_errors = self.params.get("ignore_errors", False)
|
||||
for record in self._built_object:
|
||||
if isinstance(record, Record):
|
||||
artifacts.append(record.data)
|
||||
elif ignore_errors:
|
||||
logger.error(f"Record expected, but got {record} of type {type(record)}")
|
||||
else:
|
||||
raise ValueError(f"Record expected, but got {record} of type {type(record)}")
|
||||
self.artifacts = RecordOutputResponse(records=artifacts)
|
||||
return self._built_object
|
||||
|
||||
async def _run(self, *args, **kwargs):
|
||||
if self.is_interface_component:
|
||||
if self.vertex_type in ["ChatOutput", "ChatInput"]:
|
||||
artifacts = None
|
||||
sender = self.params.get("sender", None)
|
||||
sender_name = self.params.get("sender_name", None)
|
||||
message = self.params.get(INPUT_FIELD_NAME, None)
|
||||
if isinstance(message, str):
|
||||
message = unescape_string(message)
|
||||
stream_url = None
|
||||
if isinstance(self._built_object, AIMessage):
|
||||
artifacts = ChatOutputResponse.from_message(
|
||||
self._built_object,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
)
|
||||
elif not isinstance(self._built_object, UnbuiltObject):
|
||||
if isinstance(self._built_object, dict):
|
||||
# Turn the dict into a pleasing to
|
||||
# read JSON inside a code block
|
||||
message = dict_to_codeblock(self._built_object)
|
||||
elif isinstance(self._built_object, Record):
|
||||
message = self._built_object.text
|
||||
elif isinstance(message, (AsyncIterator, Iterator)):
|
||||
stream_url = self.build_stream_url()
|
||||
message = ""
|
||||
elif not isinstance(self._built_object, str):
|
||||
message = str(self._built_object)
|
||||
# if the message is a generator or iterator
|
||||
# it means that it is a stream of messages
|
||||
else:
|
||||
message = self._built_object
|
||||
|
||||
artifacts = ChatOutputResponse(
|
||||
message=message,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
stream_url=stream_url,
|
||||
)
|
||||
|
||||
self.will_stream = stream_url is not None
|
||||
if artifacts:
|
||||
self.artifacts = artifacts.model_dump(exclude_none=True)
|
||||
if self.vertex_type in CHAT_COMPONENTS:
|
||||
message = self._process_chat_component()
|
||||
elif self.vertex_type in RECORDS_COMPONENTS:
|
||||
message = self._process_record_component()
|
||||
if isinstance(self._built_object, (AsyncIterator, Iterator)):
|
||||
if self.params["return_record"]:
|
||||
if self.params.get("return_record", False):
|
||||
self._built_object = Record(text=message, data=self.artifacts)
|
||||
else:
|
||||
self._built_object = message
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
from typing import Any, Optional, Union
|
||||
from typing import Any, Optional, Union, TYPE_CHECKING
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.runnables import Runnable
|
||||
from loguru import logger
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.utils.constants import PYTHON_BASIC_TYPES
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
|
||||
|
||||
def is_basic_type(obj):
|
||||
return type(obj) in PYTHON_BASIC_TYPES
|
||||
|
|
@ -63,3 +67,49 @@ async def generate_result(built_object: Any, inputs: dict, has_external_output:
|
|||
else:
|
||||
result = built_object
|
||||
return result
|
||||
|
||||
|
||||
def build_clean_params(target: "Vertex") -> dict:
|
||||
"""
|
||||
Cleans the parameters of the target vertex.
|
||||
"""
|
||||
# Removes all keys that the values aren't python types like str, int, bool, etc.
|
||||
params = {
|
||||
key: value for key, value in target.params.items() if isinstance(value, (str, int, bool, float, list, dict))
|
||||
}
|
||||
# if it is a list we need to check if the contents are python types
|
||||
for key, value in params.items():
|
||||
if isinstance(value, list):
|
||||
params[key] = [item for item in value if isinstance(item, (str, int, bool, float, list, dict))]
|
||||
return params
|
||||
|
||||
|
||||
def log_transaction(source: "Vertex", target: "Vertex", flow_id, status, error=None):
|
||||
"""
|
||||
Logs a transaction between two vertices.
|
||||
|
||||
Args:
|
||||
source (Vertex): The source vertex of the transaction.
|
||||
target (Vertex): The target vertex of the transaction.
|
||||
status: The status of the transaction.
|
||||
error (Optional): Any error associated with the transaction.
|
||||
|
||||
Raises:
|
||||
Exception: If there is an error while logging the transaction.
|
||||
|
||||
"""
|
||||
try:
|
||||
monitor_service = get_monitor_service()
|
||||
clean_params = build_clean_params(target)
|
||||
data = {
|
||||
"source": source.vertex_type,
|
||||
"target": target.vertex_type,
|
||||
"target_args": clean_params,
|
||||
"timestamp": monitor_service.get_timestamp(),
|
||||
"status": status,
|
||||
"error": error,
|
||||
"flow_id": flow_id,
|
||||
}
|
||||
monitor_service.add_row(table_name="transactions", data=data)
|
||||
except Exception as e:
|
||||
logger.error(f"Error logging transaction: {e}")
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ from sqlmodel import select
|
|||
from langflow.base.constants import FIELD_FORMAT_ATTRIBUTES, NODE_FORMAT_ATTRIBUTES
|
||||
from langflow.interface.types import get_all_components
|
||||
from langflow.services.database.models.flow.model import Flow, FlowCreate
|
||||
from langflow.services.database.models.folder.model import Folder, FolderCreate
|
||||
from langflow.services.deps import get_settings_service, session_scope
|
||||
|
||||
STARTER_FOLDER_NAME = "Starter Projects"
|
||||
|
||||
STARTER_FOLDER_DESCRIPTION = "Starter projects to help you get started in Langflow."
|
||||
|
||||
# In the folder ./starter_projects we have a few JSON files that represent
|
||||
# starter projects. We want to load these into the database so that users
|
||||
|
|
@ -158,6 +159,7 @@ def create_new_project(
|
|||
project_data,
|
||||
project_icon,
|
||||
project_icon_bg_color,
|
||||
new_folder_id
|
||||
):
|
||||
logger.debug(f"Creating starter project {project_name}")
|
||||
new_project = FlowCreate(
|
||||
|
|
@ -168,33 +170,41 @@ def create_new_project(
|
|||
data=project_data,
|
||||
is_component=project_is_component,
|
||||
updated_at=updated_at_datetime,
|
||||
folder=STARTER_FOLDER_NAME,
|
||||
folder_id=new_folder_id,
|
||||
)
|
||||
db_flow = Flow.model_validate(new_project, from_attributes=True)
|
||||
session.add(db_flow)
|
||||
|
||||
|
||||
def get_all_flows_similar_to_project(session, project_name):
|
||||
flows = session.exec(
|
||||
select(Flow).where(
|
||||
Flow.name == project_name,
|
||||
Flow.folder == STARTER_FOLDER_NAME,
|
||||
)
|
||||
).all()
|
||||
def get_all_flows_similar_to_project(session, folder_id):
|
||||
flows = session.exec(select(Folder).where(Folder.id == folder_id)).first().flows
|
||||
return flows
|
||||
|
||||
|
||||
def delete_start_projects(session):
|
||||
flows = session.exec(
|
||||
select(Flow).where(
|
||||
Flow.folder == STARTER_FOLDER_NAME,
|
||||
)
|
||||
).all()
|
||||
def delete_start_projects(session, folder_id):
|
||||
flows = session.exec(select(Folder).where(Folder.id == folder_id)).first().flows
|
||||
for flow in flows:
|
||||
session.delete(flow)
|
||||
session.commit()
|
||||
|
||||
|
||||
def folder_exists(session, folder_name):
|
||||
folder = session.exec(select(Folder).where(Folder.name == folder_name)).first()
|
||||
return folder is not None
|
||||
|
||||
|
||||
def create_starter_folder(session):
|
||||
if not folder_exists(session, STARTER_FOLDER_NAME):
|
||||
new_folder = FolderCreate(name=STARTER_FOLDER_NAME, description=STARTER_FOLDER_DESCRIPTION)
|
||||
db_folder = Folder.model_validate(new_folder, from_attributes=True)
|
||||
session.add(db_folder)
|
||||
session.commit()
|
||||
session.refresh(db_folder)
|
||||
return db_folder
|
||||
else:
|
||||
return session.exec(select(Folder).where(Folder.name == STARTER_FOLDER_NAME)).first()
|
||||
|
||||
|
||||
def create_or_update_starter_projects():
|
||||
components_paths = get_settings_service().settings.COMPONENTS_PATH
|
||||
try:
|
||||
|
|
@ -203,8 +213,9 @@ def create_or_update_starter_projects():
|
|||
logger.exception(f"Error loading components: {e}")
|
||||
raise e
|
||||
with session_scope() as session:
|
||||
new_folder = create_starter_folder(session)
|
||||
starter_projects = load_starter_projects()
|
||||
delete_start_projects(session)
|
||||
delete_start_projects(session, new_folder.id)
|
||||
for project_path, project in starter_projects:
|
||||
(
|
||||
project_name,
|
||||
|
|
@ -224,7 +235,7 @@ def create_or_update_starter_projects():
|
|||
|
||||
update_project_file(project_path, project, updated_project_data)
|
||||
if project_name and project_data:
|
||||
for existing_project in get_all_flows_similar_to_project(session, project_name):
|
||||
for existing_project in get_all_flows_similar_to_project(session, new_folder.id):
|
||||
session.delete(existing_project)
|
||||
|
||||
create_new_project(
|
||||
|
|
@ -236,4 +247,5 @@ def create_or_update_starter_projects():
|
|||
project_data,
|
||||
project_icon,
|
||||
project_icon_bg_color,
|
||||
new_folder.id
|
||||
)
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@
|
|||
"load_from_db": true,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"stream": {
|
||||
"type": "bool",
|
||||
|
|
@ -716,9 +716,9 @@
|
|||
"edges": [
|
||||
{
|
||||
"source": "OpenAIModel-k39HS",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-k39HSœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153}",
|
||||
"target": "ChatOutput-njtka",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-njtkaœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-njtka\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -736,13 +736,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-k39HS{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-k39HSœ}-ChatOutput-njtka{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-njtkaœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-k39HS{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153}-ChatOutput-njtka{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-njtka\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-uxBqP",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-uxBqPœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153}",
|
||||
"target": "OpenAIModel-k39HS",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-k39HSœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -760,13 +760,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-uxBqP{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-uxBqPœ}-OpenAIModel-k39HS{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-k39HSœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-uxBqP{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153}-OpenAIModel-k39HS{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "ChatInput-P3fgL",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-P3fgLœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-P3fgL\u0153}",
|
||||
"target": "Prompt-uxBqP",
|
||||
"targetHandle": "{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-uxBqPœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153user_input\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "user_input",
|
||||
|
|
@ -784,7 +784,7 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-ChatInput-P3fgL{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-P3fgLœ}-Prompt-uxBqP{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-uxBqPœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-ChatInput-P3fgL{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-P3fgL\u0153}-Prompt-uxBqP{\u0153fieldName\u0153:\u0153user_input\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
|
|
|
|||
|
|
@ -566,7 +566,7 @@
|
|||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"stream": {
|
||||
"type": "bool",
|
||||
|
|
@ -854,9 +854,9 @@
|
|||
{
|
||||
"source": "URL-HYPkR",
|
||||
"target": "Prompt-Rse03",
|
||||
"sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-HYPkRœ}",
|
||||
"targetHandle": "{œfieldNameœ:œreference_2œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"id": "reactflow__edge-URL-HYPkR{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-HYPkRœ}-Prompt-Rse03{œfieldNameœ:œreference_2œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-HYPkR\u0153}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153reference_2\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"id": "reactflow__edge-URL-HYPkR{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-HYPkR\u0153}-Prompt-Rse03{\u0153fieldName\u0153:\u0153reference_2\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "reference_2",
|
||||
|
|
@ -878,9 +878,9 @@
|
|||
},
|
||||
{
|
||||
"source": "OpenAIModel-gi29P",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gi29Pœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153}",
|
||||
"target": "ChatOutput-JPlxl",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JPlxlœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-JPlxl\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -898,13 +898,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-gi29P{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gi29Pœ}-ChatOutput-JPlxl{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JPlxlœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-gi29P{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153}-ChatOutput-JPlxl{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-JPlxl\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "URL-2cX90",
|
||||
"sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-2cX90œ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-2cX90\u0153}",
|
||||
"target": "Prompt-Rse03",
|
||||
"targetHandle": "{œfieldNameœ:œreference_1œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153reference_1\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "reference_1",
|
||||
|
|
@ -922,13 +922,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-URL-2cX90{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-2cX90œ}-Prompt-Rse03{œfieldNameœ:œreference_1œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-URL-2cX90{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-2cX90\u0153}-Prompt-Rse03{\u0153fieldName\u0153:\u0153reference_1\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "TextInput-og8Or",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-og8Orœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-og8Or\u0153}",
|
||||
"target": "Prompt-Rse03",
|
||||
"targetHandle": "{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153instructions\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "instructions",
|
||||
|
|
@ -946,13 +946,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-TextInput-og8Or{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-og8Orœ}-Prompt-Rse03{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-TextInput-og8Or{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-og8Or\u0153}-Prompt-Rse03{\u0153fieldName\u0153:\u0153instructions\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-Rse03",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-Rse03œ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153}",
|
||||
"target": "OpenAIModel-gi29P",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gi29Pœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -970,7 +970,7 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-Rse03{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-Rse03œ}-OpenAIModel-gi29P{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gi29Pœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"id": "reactflow__edge-Prompt-Rse03{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153}-OpenAIModel-gi29P{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"selected": false
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -711,7 +711,7 @@
|
|||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"stream": {
|
||||
"type": "bool",
|
||||
|
|
@ -825,9 +825,9 @@
|
|||
"edges": [
|
||||
{
|
||||
"source": "ChatInput-MsSJ9",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œRecordœ,œTextœ,œobjectœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-MsSJ9œ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Record\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-MsSJ9\u0153}",
|
||||
"target": "Prompt-tHwPf",
|
||||
"targetHandle": "{œfieldNameœ:œQuestionœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153Question\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "Question",
|
||||
|
|
@ -845,13 +845,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-ChatInput-MsSJ9{œbaseClassesœ:[œstrœ,œRecordœ,œTextœ,œobjectœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-MsSJ9œ}-Prompt-tHwPf{œfieldNameœ:œQuestionœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-ChatInput-MsSJ9{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Record\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-MsSJ9\u0153}-Prompt-tHwPf{\u0153fieldName\u0153:\u0153Question\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "File-6TEsD",
|
||||
"sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-6TEsDœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153File\u0153,\u0153id\u0153:\u0153File-6TEsD\u0153}",
|
||||
"target": "Prompt-tHwPf",
|
||||
"targetHandle": "{œfieldNameœ:œDocumentœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153Document\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "Document",
|
||||
|
|
@ -869,13 +869,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-File-6TEsD{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-6TEsDœ}-Prompt-tHwPf{œfieldNameœ:œDocumentœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-File-6TEsD{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153File\u0153,\u0153id\u0153:\u0153File-6TEsD\u0153}-Prompt-tHwPf{\u0153fieldName\u0153:\u0153Document\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-tHwPf",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-tHwPfœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153}",
|
||||
"target": "OpenAIModel-Bt067",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Bt067œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -893,13 +893,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-tHwPf{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-tHwPfœ}-OpenAIModel-Bt067{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Bt067œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-tHwPf{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153}-OpenAIModel-Bt067{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "OpenAIModel-Bt067",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Bt067œ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153}",
|
||||
"target": "ChatOutput-F5Awj",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-F5Awjœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-F5Awj\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -917,7 +917,7 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-Bt067{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Bt067œ}-ChatOutput-F5Awj{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-F5Awjœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-Bt067{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153}-ChatOutput-F5Awj{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-F5Awj\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"id": "08d5cccf-d098-4367-b14b-1078429c9ed9",
|
||||
"icon": "🤖",
|
||||
"icon": "\ud83e\udd16",
|
||||
"icon_bg_color": "#FFD700",
|
||||
"data": {
|
||||
"nodes": [
|
||||
|
|
@ -792,7 +792,7 @@
|
|||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"stream": {
|
||||
"type": "bool",
|
||||
|
|
@ -1003,9 +1003,9 @@
|
|||
"edges": [
|
||||
{
|
||||
"source": "MemoryComponent-cdA1J",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}",
|
||||
"target": "Prompt-ODkUx",
|
||||
"targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153context\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "context",
|
||||
|
|
@ -1023,14 +1023,14 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-Prompt-ODkUx{œfieldNameœ:œcontextœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"id": "reactflow__edge-MemoryComponent-cdA1J{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}-Prompt-ODkUx{\u0153fieldName\u0153:\u0153context\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"source": "ChatInput-t7F8v",
|
||||
"sourceHandle": "{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-t7F8vœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-t7F8v\u0153}",
|
||||
"target": "Prompt-ODkUx",
|
||||
"targetHandle": "{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153user_message\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "user_message",
|
||||
|
|
@ -1048,14 +1048,14 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-ChatInput-t7F8v{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-t7F8vœ}-Prompt-ODkUx{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"id": "reactflow__edge-ChatInput-t7F8v{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-t7F8v\u0153}-Prompt-ODkUx{\u0153fieldName\u0153:\u0153user_message\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"source": "Prompt-ODkUx",
|
||||
"sourceHandle": "{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-ODkUxœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153}",
|
||||
"target": "OpenAIModel-9RykF",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-9RykFœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1073,13 +1073,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-ODkUx{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-ODkUxœ}-OpenAIModel-9RykF{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-9RykFœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-ODkUx{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153}-OpenAIModel-9RykF{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "OpenAIModel-9RykF",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-9RykFœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153object\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153}",
|
||||
"target": "ChatOutput-P1jEe",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-P1jEeœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-P1jEe\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1097,13 +1097,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-9RykF{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-9RykFœ}-ChatOutput-P1jEe{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-P1jEeœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-9RykF{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153object\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153}-ChatOutput-P1jEe{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-P1jEe\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "MemoryComponent-cdA1J",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}",
|
||||
"target": "TextOutput-vrs6T",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-vrs6Tœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-vrs6T\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1121,7 +1121,7 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-foreground stroke-connection",
|
||||
"id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-TextOutput-vrs6T{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-vrs6Tœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-MemoryComponent-cdA1J{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}-TextOutput-vrs6T{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-vrs6T\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
|
|
|
|||
|
|
@ -911,7 +911,7 @@
|
|||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
"value": "OPENAI_API_KEY"
|
||||
},
|
||||
"stream": {
|
||||
"type": "bool",
|
||||
|
|
@ -1382,9 +1382,9 @@
|
|||
"edges": [
|
||||
{
|
||||
"source": "TextInput-sptaH",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-sptaHœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-sptaH\u0153}",
|
||||
"target": "Prompt-amqBu",
|
||||
"targetHandle": "{œfieldNameœ:œdocumentœ,œidœ:œPrompt-amqBuœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153document\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "document",
|
||||
|
|
@ -1402,13 +1402,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-TextInput-sptaH{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-sptaHœ}-Prompt-amqBu{œfieldNameœ:œdocumentœ,œidœ:œPrompt-amqBuœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-TextInput-sptaH{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-sptaH\u0153}-Prompt-amqBu{\u0153fieldName\u0153:\u0153document\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-amqBu",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}",
|
||||
"target": "TextOutput-2MS4a",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-2MS4aœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-2MS4a\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1426,13 +1426,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-amqBu{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}-TextOutput-2MS4a{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-2MS4aœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-amqBu{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}-TextOutput-2MS4a{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-2MS4a\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-amqBu",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}",
|
||||
"target": "OpenAIModel-uYXZJ",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uYXZJœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1450,13 +1450,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-amqBu{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}-OpenAIModel-uYXZJ{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uYXZJœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-amqBu{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}-OpenAIModel-uYXZJ{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "OpenAIModel-uYXZJ",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}",
|
||||
"target": "Prompt-gTNiz",
|
||||
"targetHandle": "{œfieldNameœ:œsummaryœ,œidœ:œPrompt-gTNizœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153summary\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "summary",
|
||||
|
|
@ -1474,13 +1474,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-uYXZJ{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}-Prompt-gTNiz{œfieldNameœ:œsummaryœ,œidœ:œPrompt-gTNizœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-uYXZJ{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}-Prompt-gTNiz{\u0153fieldName\u0153:\u0153summary\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "OpenAIModel-uYXZJ",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}",
|
||||
"target": "ChatOutput-EJkG3",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-EJkG3œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-EJkG3\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1498,13 +1498,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-uYXZJ{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}-ChatOutput-EJkG3{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-EJkG3œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-uYXZJ{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}-ChatOutput-EJkG3{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-EJkG3\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-gTNiz",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}",
|
||||
"target": "TextOutput-MUDOR",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-MUDORœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-MUDOR\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1522,13 +1522,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-gTNiz{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}-TextOutput-MUDOR{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-MUDORœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-gTNiz{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}-TextOutput-MUDOR{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-MUDOR\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "Prompt-gTNiz",
|
||||
"sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}",
|
||||
"target": "OpenAIModel-XawYB",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-XawYBœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1546,13 +1546,13 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-Prompt-gTNiz{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}-OpenAIModel-XawYB{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-XawYBœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-Prompt-gTNiz{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}-OpenAIModel-XawYB{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
},
|
||||
{
|
||||
"source": "OpenAIModel-XawYB",
|
||||
"sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-XawYBœ}",
|
||||
"sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153}",
|
||||
"target": "ChatOutput-DNmvg",
|
||||
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-DNmvgœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
|
||||
"targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-DNmvg\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}",
|
||||
"data": {
|
||||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
|
|
@ -1570,7 +1570,7 @@
|
|||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-OpenAIModel-XawYB{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-XawYBœ}-ChatOutput-DNmvg{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-DNmvgœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
"id": "reactflow__edge-OpenAIModel-XawYB{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153}-ChatOutput-DNmvg{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-DNmvg\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}"
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5,7 +5,6 @@ from langchain.agents.agent_toolkits import VectorStoreInfo, VectorStoreRouterTo
|
|||
from langchain.agents.agent_toolkits.vectorstore.prompt import PREFIX as VECTORSTORE_PREFIX
|
||||
from langchain.agents.agent_toolkits.vectorstore.prompt import ROUTER_PREFIX as VECTORSTORE_ROUTER_PREFIX
|
||||
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.chains.llm import LLMChain
|
||||
from langchain_community.utilities import SQLDatabase
|
||||
from langchain.tools.sql_database.prompt import QUERY_CHECKER
|
||||
|
|
@ -18,6 +17,14 @@ from langchain_experimental.agents.agent_toolkits.pandas.prompt import SUFFIX_WI
|
|||
from langchain_experimental.tools.python.tool import PythonAstREPLTool
|
||||
|
||||
from langflow.interface.base import CustomAgentExecutor
|
||||
from langchain_community.tools import (
|
||||
InfoSQLDatabaseTool,
|
||||
ListSQLDatabaseTool,
|
||||
QuerySQLCheckerTool,
|
||||
QuerySQLDataBaseTool,
|
||||
)
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
|
||||
class JsonAgent(CustomAgentExecutor):
|
||||
|
|
@ -165,17 +172,6 @@ class SQLAgent(CustomAgentExecutor):
|
|||
db = SQLDatabase.from_uri(database_uri)
|
||||
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
|
||||
|
||||
# The right code should be this, but there is a problem with tools = toolkit.get_tools()
|
||||
# related to `OPENAI_API_KEY`
|
||||
# return create_sql_agent(llm=llm, toolkit=toolkit, verbose=True)
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.tools.sql_database.tool import (
|
||||
InfoSQLDatabaseTool,
|
||||
ListSQLDatabaseTool,
|
||||
QuerySQLCheckerTool,
|
||||
QuerySQLDataBaseTool,
|
||||
)
|
||||
|
||||
llmchain = LLMChain(
|
||||
llm=llm,
|
||||
prompt=PromptTemplate(template=QUERY_CHECKER, input_variables=["query", "dialect"]),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from langchain.chains.llm import LLMChain
|
||||
from langchain.agents import AgentExecutor, ZeroShotAgent
|
||||
from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
|
||||
from langchain.agents.agent_toolkits.json.toolkit import JsonToolkit
|
||||
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain_community.agent_toolkits import JsonToolkit
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
|
||||
|
||||
class MalfoyAgent(AgentExecutor):
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
from typing import Dict, Optional, Type, Union
|
||||
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.chains import ConversationChain
|
||||
from langchain.chains.question_answering import load_qa_chain
|
||||
from langchain.memory.buffer import ConversationBufferMemory
|
||||
from langchain.schema import BaseMemory
|
||||
from pydantic.v1 import Field, root_validator
|
||||
|
||||
from langflow.interface.base import CustomChain
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
from langchain_core.memory import BaseMemory
|
||||
|
||||
DEFAULT_SUFFIX = """"
|
||||
Current conversation:
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ from typing import Any
|
|||
|
||||
from langchain import llms, memory, text_splitter
|
||||
from langchain_community import agent_toolkits, document_loaders, embeddings
|
||||
from langchain_community.chat_models import AzureChatOpenAI, ChatAnthropic, ChatOpenAI, ChatVertexAI
|
||||
from langchain_community.chat_models import ChatVertexAI
|
||||
|
||||
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
||||
from langflow.interface.chains.custom import CUSTOM_CHAINS
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_openai import AzureChatOpenAI, ChatOpenAI
|
||||
|
||||
# LLMs
|
||||
llm_type_to_cls_dict = {}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import importlib
|
|||
from typing import Any, Type
|
||||
|
||||
from langchain.agents import Agent
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.tools import BaseTool
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
|
||||
from langflow.interface.wrappers.base import wrapper_creator
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_core.tools import BaseTool
|
||||
|
||||
|
||||
def import_module(module_path: str) -> Any:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
import inspect
|
||||
import json
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type
|
||||
|
||||
import orjson
|
||||
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 langchain.chains.base import Chain
|
||||
from langchain.document_loaders.base import BaseLoader
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_core.documents import Document
|
||||
from loguru import logger
|
||||
from pydantic import ValidationError
|
||||
|
|
@ -26,6 +23,11 @@ from langflow.interface.wrappers.base import wrapper_creator
|
|||
from langflow.schema.schema import Record
|
||||
from langflow.utils import validate
|
||||
from langflow.utils.util import unescape_string
|
||||
from langchain_community.agent_toolkits.base import BaseToolkit
|
||||
from langchain_core.document_loaders import BaseLoader
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_core.vectorstores import VectorStore
|
||||
from langchain_text_splitters import Language
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.custom import CustomComponent
|
||||
|
|
@ -34,6 +36,7 @@ if TYPE_CHECKING:
|
|||
|
||||
async def instantiate_class(
|
||||
vertex: "Vertex",
|
||||
fallback_to_env_vars,
|
||||
user_id=None,
|
||||
) -> Any:
|
||||
"""Instantiate class from module type and key, and params"""
|
||||
|
|
@ -56,7 +59,7 @@ async def instantiate_class(
|
|||
if not base_type:
|
||||
raise ValueError("No base type provided for vertex")
|
||||
if base_type == "custom_components":
|
||||
return await instantiate_custom_component(params, user_id, vertex)
|
||||
return await instantiate_custom_component(params, user_id, vertex, fallback_to_env_vars=fallback_to_env_vars)
|
||||
class_object = import_by_type(_type=base_type, name=vertex_type)
|
||||
return await instantiate_based_on_type(
|
||||
class_object=class_object,
|
||||
|
|
@ -65,6 +68,7 @@ async def instantiate_class(
|
|||
params=params,
|
||||
user_id=user_id,
|
||||
vertex=vertex,
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -92,14 +96,7 @@ def convert_kwargs(params):
|
|||
return params
|
||||
|
||||
|
||||
async def instantiate_based_on_type(
|
||||
class_object,
|
||||
base_type,
|
||||
node_type,
|
||||
params,
|
||||
user_id,
|
||||
vertex,
|
||||
):
|
||||
async def instantiate_based_on_type(class_object, base_type, node_type, params, user_id, vertex, fallback_to_env_vars):
|
||||
if base_type == "agents":
|
||||
return instantiate_agent(node_type, class_object, params)
|
||||
elif base_type == "prompts":
|
||||
|
|
@ -131,33 +128,49 @@ async def instantiate_based_on_type(
|
|||
elif base_type == "memory":
|
||||
return instantiate_memory(node_type, class_object, params)
|
||||
elif base_type == "custom_components":
|
||||
return await instantiate_custom_component(
|
||||
params,
|
||||
user_id,
|
||||
vertex,
|
||||
)
|
||||
return await instantiate_custom_component(params, user_id, vertex, fallback_to_env_vars=fallback_to_env_vars)
|
||||
elif base_type == "wrappers":
|
||||
return instantiate_wrapper(node_type, class_object, params)
|
||||
else:
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def update_params_with_load_from_db_fields(custom_component: "CustomComponent", params, load_from_db_fields):
|
||||
def update_params_with_load_from_db_fields(
|
||||
custom_component: "CustomComponent", params, load_from_db_fields, fallback_to_env_vars=False
|
||||
):
|
||||
# For each field in load_from_db_fields, we will check if it's in the params
|
||||
# and if it is, we will get the value from the custom_component.keys(name)
|
||||
# and update the params with the value
|
||||
for field in load_from_db_fields:
|
||||
if field in params:
|
||||
try:
|
||||
key = custom_component.variables(params[field])
|
||||
params[field] = key if key else params[field]
|
||||
key = None
|
||||
try:
|
||||
key = custom_component.variables(params[field])
|
||||
except ValueError as e:
|
||||
# check if "User id is not set" is in the error message
|
||||
if "User id is not set" in str(e) and not fallback_to_env_vars:
|
||||
raise e
|
||||
logger.debug(str(e))
|
||||
if fallback_to_env_vars and key is None:
|
||||
var = os.getenv(params[field])
|
||||
if var is None:
|
||||
raise ValueError(f"Environment variable {params[field]} is not set.")
|
||||
key = var
|
||||
logger.info(f"Using environment variable {params[field]} for {field}")
|
||||
if key is None:
|
||||
logger.warning(f"Could not get value for {field}. Setting it to None.")
|
||||
params[field] = key
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Failed to get value for {field} from custom component. Error: {exc}")
|
||||
pass
|
||||
logger.error(f"Failed to get value for {field} from custom component. Setting it to None. Error: {exc}")
|
||||
|
||||
params[field] = None
|
||||
|
||||
return params
|
||||
|
||||
|
||||
async def instantiate_custom_component(params, user_id, vertex):
|
||||
async def instantiate_custom_component(params, user_id, vertex, fallback_to_env_vars: bool = False):
|
||||
params_copy = params.copy()
|
||||
class_object: Type["CustomComponent"] = eval_custom_component_code(params_copy.pop("code"))
|
||||
custom_component: "CustomComponent" = class_object(
|
||||
|
|
@ -166,7 +179,9 @@ async def instantiate_custom_component(params, user_id, vertex):
|
|||
vertex=vertex,
|
||||
selected_output_type=vertex.selected_output_type,
|
||||
)
|
||||
params_copy = update_params_with_load_from_db_fields(custom_component, params_copy, vertex.load_from_db_fields)
|
||||
params_copy = update_params_with_load_from_db_fields(
|
||||
custom_component, params_copy, vertex.load_from_db_fields, fallback_to_env_vars
|
||||
)
|
||||
|
||||
if "retriever" in params_copy and hasattr(params_copy["retriever"], "as_retriever"):
|
||||
params_copy["retriever"] = params_copy["retriever"].as_retriever()
|
||||
|
|
@ -416,8 +431,6 @@ def instantiate_textsplitter(
|
|||
params["separators"] = [unescape_string(separator) for separator in params["separators"]]
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ from typing import Any, Dict, List
|
|||
|
||||
import orjson
|
||||
from langchain.agents import ZeroShotAgent
|
||||
from langchain.schema import BaseOutputParser, Document
|
||||
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.output_parsers import BaseOutputParser
|
||||
|
||||
|
||||
def handle_node_type(node_type, class_object, params: Dict):
|
||||
|
|
|
|||
|
|
@ -5,14 +5,13 @@ import orjson
|
|||
from langchain_community.vectorstores import (
|
||||
FAISS,
|
||||
Chroma,
|
||||
ElasticsearchStore,
|
||||
MongoDBAtlasVectorSearch,
|
||||
Pinecone,
|
||||
Qdrant,
|
||||
SupabaseVectorStore,
|
||||
Weaviate,
|
||||
)
|
||||
from langchain_core.documents import Document
|
||||
from langchain_pinecone import Pinecone
|
||||
|
||||
|
||||
def docs_in_params(params: dict) -> bool:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from typing import Dict, List, Optional, Type
|
||||
|
||||
from langchain.prompts import PromptTemplate
|
||||
from pydantic.v1 import root_validator
|
||||
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
# Steps to create a BaseCustomPrompt:
|
||||
# 1. Create a prompt template that endes with:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
from langchain import tools
|
||||
from langchain.agents import Tool
|
||||
from langchain.agents.load_tools import _BASE_TOOLS, _EXTRA_LLM_TOOLS, _EXTRA_OPTIONAL_TOOLS, _LLM_TOOLS
|
||||
from langchain_community.tools.json.tool import JsonSpec
|
||||
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.interface.tools.custom import PythonFunctionTool
|
||||
from langchain_core.tools import Tool
|
||||
|
||||
FILE_TOOLS = {"JsonSpec": JsonSpec}
|
||||
CUSTOM_TOOLS = {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
from typing import Callable, Optional
|
||||
|
||||
from langchain.agents.tools import Tool
|
||||
from pydantic.v1 import BaseModel, validator
|
||||
|
||||
from langflow.interface.custom.utils import get_function
|
||||
from langflow.utils import validate
|
||||
from langchain_core.tools import Tool
|
||||
|
||||
|
||||
class Function(BaseModel):
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import ast
|
|||
import inspect
|
||||
import textwrap
|
||||
from typing import Dict, Union
|
||||
|
||||
from langchain.agents.tools import Tool
|
||||
from langchain_core.tools import Tool
|
||||
|
||||
|
||||
def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ from typing import Dict
|
|||
|
||||
import yaml
|
||||
from docstring_parser import parse
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langflow.services.chat.config import ChatConfig
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.utils.util import format_dict, get_base_classes, get_default_factory
|
||||
from loguru import logger
|
||||
from PIL.Image import Image
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
|
||||
|
||||
def load_file_into_dict(file_path: str) -> dict:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import warnings
|
||||
from typing import Optional, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ def get_messages(
|
|||
return records
|
||||
|
||||
|
||||
def add_messages(records: Union[list[Record], Record]):
|
||||
def add_messages(records: Union[list[Record], Record], flow_id: Optional[str] = None):
|
||||
"""
|
||||
Add a message to the monitor service.
|
||||
"""
|
||||
|
|
@ -76,7 +76,8 @@ def add_messages(records: Union[list[Record], Record]):
|
|||
|
||||
messages: list[MessageModel] = []
|
||||
for record in records:
|
||||
messages.append(MessageModel.from_record(record))
|
||||
record.timestamp = monitor_service.get_timestamp()
|
||||
messages.append(MessageModel.from_record(record, flow_id=flow_id))
|
||||
|
||||
for message in messages:
|
||||
try:
|
||||
|
|
@ -107,8 +108,24 @@ def store_message(
|
|||
session_id: Optional[str] = None,
|
||||
sender: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
) -> list[Record]:
|
||||
flow_id: Optional[str] = None,
|
||||
) -> List[Record]:
|
||||
"""
|
||||
Stores a message in the memory.
|
||||
|
||||
Args:
|
||||
message (Union[str, Record]): The message to be stored. It can be either a string or a Record object.
|
||||
session_id (Optional[str]): The session ID associated with the message.
|
||||
sender (Optional[str]): The sender ID associated with the message.
|
||||
sender_name (Optional[str]): The name of the sender associated with the message.
|
||||
flow_id (Optional[str]): The flow ID associated with the message. When running from the CustomComponent you can access this using `self.graph.flow_id`.
|
||||
|
||||
Returns:
|
||||
List[Record]: A list of records containing the stored message.
|
||||
|
||||
Raises:
|
||||
ValueError: If any of the required parameters (session_id, sender, sender_name) is not provided.
|
||||
"""
|
||||
if not message:
|
||||
warnings.warn("No message provided.")
|
||||
return []
|
||||
|
|
@ -135,4 +152,4 @@ def store_message(
|
|||
},
|
||||
)
|
||||
|
||||
return add_messages([record])
|
||||
return add_messages([record], flow_id=flow_id)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from typing import TYPE_CHECKING, List, Union
|
||||
|
||||
from langchain.agents.agent import AgentExecutor
|
||||
from langchain.callbacks.base import BaseCallbackHandler
|
||||
from loguru import logger
|
||||
|
||||
from langflow.processing.process import fix_memory_inputs, format_actions
|
||||
from langflow.services.deps import get_plugins_service
|
||||
from langchain_core.callbacks import BaseCallbackHandler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langfuse.callback import CallbackHandler # type: ignore
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ def run_flow_from_json(
|
|||
env_file: Optional[str] = None,
|
||||
cache: Optional[str] = None,
|
||||
disable_logs: Optional[bool] = True,
|
||||
fallback_to_env_vars: bool = False,
|
||||
) -> List[RunOutputs]:
|
||||
"""
|
||||
Run a flow from a JSON file or dictionary.
|
||||
|
|
@ -98,6 +99,7 @@ def run_flow_from_json(
|
|||
env_file (Optional[str], optional): The environment file to load. Defaults to None.
|
||||
cache (Optional[str], optional): The cache directory to use. Defaults to None.
|
||||
disable_logs (Optional[bool], optional): Whether to disable logs. Defaults to True.
|
||||
fallback_to_env_vars (bool, optional): Whether Global Variables should fallback to environment variables if not found. Defaults to False.
|
||||
|
||||
Returns:
|
||||
List[RunOutputs]: A list of RunOutputs objects representing the results of running the flow.
|
||||
|
|
@ -127,5 +129,6 @@ def run_flow_from_json(
|
|||
input_type=input_type,
|
||||
output_type=output_type,
|
||||
output_component=output_component,
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
|||
|
||||
|
||||
from langchain.agents import AgentExecutor
|
||||
from langchain.schema import AgentAction
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
|
@ -13,6 +12,7 @@ from langflow.interface.run import get_memory_key, update_memory_keys
|
|||
from langflow.schema.graph import InputValue, Tweaks
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.session.service import SessionService
|
||||
from langchain_core.agents import AgentAction
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -175,6 +175,7 @@ def run_graph(
|
|||
input_value: str,
|
||||
input_type: str,
|
||||
output_type: str,
|
||||
fallback_to_env_vars: bool = False,
|
||||
output_component: Optional[str] = None,
|
||||
) -> List[RunOutputs]:
|
||||
"""
|
||||
|
|
@ -218,6 +219,7 @@ def run_graph(
|
|||
outputs or [],
|
||||
stream=False,
|
||||
session_id="",
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
return run_outputs
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import copy
|
||||
import json
|
||||
from typing import Literal, Optional, cast
|
||||
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
||||
from pydantic import BaseModel, model_validator
|
||||
from langchain_core.messages import HumanMessage, AIMessage
|
||||
|
||||
|
||||
class Record(BaseModel):
|
||||
|
|
@ -163,23 +163,15 @@ class Record(BaseModel):
|
|||
# Create a new Record object with a deep copy of the data dictionary
|
||||
return Record(data=copy.deepcopy(self.data, memo), text_key=self.text_key, default_value=self.default_value)
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
Returns a string representation of the Record, including text and data.
|
||||
"""
|
||||
# Assuming a method to dump model data as JSON string exists.
|
||||
# If it doesn't, you might need to implement it or use json.dumps() directly.
|
||||
# build the string considering all keys in the data dictionary
|
||||
prefix = "Record("
|
||||
suffix = ")"
|
||||
text = f"text_key={self.text_key}, "
|
||||
text += ", ".join([f"{k}={v}" for k, v in self.data.items()])
|
||||
return prefix + text + suffix
|
||||
|
||||
# check which attributes the Record has by checking the keys in the data dictionary
|
||||
def __dir__(self):
|
||||
return super().__dir__() + list(self.data.keys())
|
||||
|
||||
def __str__(self) -> str:
|
||||
# return a JSON string representation of the Record atributes
|
||||
|
||||
return json.dumps(self.data)
|
||||
|
||||
|
||||
INPUT_FIELD_NAME = "input_value"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from .api_key import ApiKey
|
||||
from .flow import Flow
|
||||
from .folder import Folder
|
||||
from .user import User
|
||||
from .variable import Variable
|
||||
|
||||
__all__ = ["Flow", "User", "ApiKey", "Variable"]
|
||||
__all__ = ["Flow", "User", "ApiKey", "Variable", "Folder"]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from sqlmodel import JSON, Column, Field, Relationship, SQLModel
|
|||
from langflow.schema.schema import Record
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.models.folder import Folder
|
||||
from langflow.services.database.models.user import User
|
||||
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ class FlowBase(SQLModel):
|
|||
data: Optional[Dict] = Field(default=None, nullable=True)
|
||||
is_component: Optional[bool] = Field(default=False, nullable=True)
|
||||
updated_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(timezone.utc), nullable=True)
|
||||
folder: Optional[str] = Field(default=None, nullable=True)
|
||||
folder_id: Optional[UUID] = Field(default=None, nullable=True)
|
||||
|
||||
@field_validator("icon_bg_color")
|
||||
def validate_icon_bg_color(cls, v):
|
||||
|
|
@ -112,6 +113,8 @@ class Flow(FlowBase, table=True):
|
|||
data: Optional[Dict] = Field(default=None, sa_column=Column(JSON))
|
||||
user_id: Optional[UUID] = Field(index=True, foreign_key="user.id", nullable=True)
|
||||
user: "User" = Relationship(back_populates="flows")
|
||||
folder_id: Optional[UUID] = Field(default=None, foreign_key="folder.id", nullable=True, index=True)
|
||||
folder: Optional["Folder"] = Relationship(back_populates="flows")
|
||||
|
||||
def to_record(self):
|
||||
serialized = self.model_dump()
|
||||
|
|
@ -128,14 +131,17 @@ class Flow(FlowBase, table=True):
|
|||
|
||||
class FlowCreate(FlowBase):
|
||||
user_id: Optional[UUID] = None
|
||||
folder_id: Optional[UUID] = None
|
||||
|
||||
|
||||
class FlowRead(FlowBase):
|
||||
id: UUID
|
||||
user_id: Optional[UUID] = Field()
|
||||
folder_id: Optional[UUID] = Field()
|
||||
|
||||
|
||||
class FlowUpdate(SQLModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
data: Optional[Dict] = None
|
||||
folder_id: Optional[UUID] = None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
from .model import Folder, FolderCreate, FolderRead, FolderUpdate
|
||||
|
||||
__all__ = ["Folder", "FolderCreate", "FolderRead", "FolderUpdate"]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
DEFAULT_FOLDER_DESCRIPTION = "Manage your personal projects. Download and upload entire collections."
|
||||
DEFAULT_FOLDER_NAME = "My Projects"
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue