Merge remote-tracking branch 'origin/dev' into chatImg
This commit is contained in:
commit
a6c038a629
471 changed files with 20280 additions and 11322 deletions
1
src/backend/.gitignore
vendored
1
src/backend/.gitignore
vendored
|
|
@ -131,3 +131,4 @@ dmypy.json
|
|||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
*.db
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
FROM logspace/backend_build as backend_build
|
||||
FROM langflowai/backend_build as backend_build
|
||||
|
||||
FROM python:3.10-slim
|
||||
WORKDIR /app
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import platform
|
|||
import socket
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -16,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
|
||||
|
|
@ -431,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."),
|
||||
|
|
@ -468,7 +511,9 @@ def migration(
|
|||
|
||||
|
||||
def main():
|
||||
app()
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -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 default_fields column
|
||||
|
||||
Revision ID: 1f4d6df60295
|
||||
Revises: 6e7b581b5648
|
||||
Create Date: 2024-04-29 09:49:46.864145
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "1f4d6df60295"
|
||||
down_revision: Union[str, None] = "6e7b581b5648"
|
||||
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! ###
|
||||
column_names = [column["name"] for column in inspector.get_columns("variable")]
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if "default_fields" not in column_names:
|
||||
batch_op.add_column(sa.Column("default_fields", sa.JSON(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
column_names = [column["name"] for column in inspector.get_columns("variable")]
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if "default_fields" in column_names:
|
||||
batch_op.drop_column("default_fields")
|
||||
|
||||
# ### 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 ###
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
"""Fix nullable
|
||||
|
||||
Revision ID: 6e7b581b5648
|
||||
Revises: 58b28437a398
|
||||
Create Date: 2024-04-30 09:17:45.024688
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "6e7b581b5648"
|
||||
down_revision: Union[str, None] = "58b28437a398"
|
||||
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! ###
|
||||
columns = inspector.get_columns("apikey")
|
||||
column_names = {column["name"]: column for column in columns}
|
||||
with op.batch_alter_table("apikey", schema=None) as batch_op:
|
||||
created_at_column = [column for column in columns if column["name"] == "created_at"][0]
|
||||
if "created_at" in column_names and created_at_column.get("nullable"):
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=False,
|
||||
existing_server_default=sa.text("(CURRENT_TIMESTAMP)"), # type: ignore
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# table_names = inspector.get_table_names()
|
||||
columns = inspector.get_columns("apikey")
|
||||
column_names = {column["name"]: column for column in columns}
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("apikey", schema=None) as batch_op:
|
||||
created_at_column = [column for column in columns if column["name"] == "created_at"][0]
|
||||
if "created_at" in column_names and not created_at_column.get("nullable"):
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=sa.DATETIME(),
|
||||
nullable=True,
|
||||
existing_server_default=sa.text("(CURRENT_TIMESTAMP)"), # type: ignore
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
"""Set name and value to not nullable
|
||||
|
||||
Revision ID: c153816fd85f
|
||||
Revises: 1f4d6df60295
|
||||
Create Date: 2024-04-30 14:31:23.898995
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "c153816fd85f"
|
||||
down_revision: Union[str, None] = "1f4d6df60295"
|
||||
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! ###
|
||||
columns = inspector.get_columns("variable")
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
name_column = [column for column in columns if column["name"] == "name"][0]
|
||||
if name_column and name_column["nullable"]:
|
||||
batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=False)
|
||||
value_column = [column for column in columns if column["name"] == "value"][0]
|
||||
if value_column and value_column["nullable"]:
|
||||
batch_op.alter_column("value", existing_type=sa.VARCHAR(), nullable=False)
|
||||
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
columns = inspector.get_columns("variable")
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
name_column = [column for column in columns if column["name"] == "name"][0]
|
||||
if name_column and not name_column["nullable"]:
|
||||
batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=True)
|
||||
value_column = [column for column in columns if column["name"] == "value"][0]
|
||||
if value_column and not value_column["nullable"]:
|
||||
batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=True)
|
||||
|
||||
# ### 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)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
|
@ -140,7 +141,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
|
||||
|
||||
|
|
@ -201,21 +205,27 @@ def format_elapsed_time(elapsed_time: float) -> str:
|
|||
return f"{minutes} {minutes_unit}, {seconds} {seconds_unit}"
|
||||
|
||||
|
||||
async def build_and_cache_graph(
|
||||
async def build_and_cache_graph_from_db(
|
||||
flow_id: str,
|
||||
session: Session,
|
||||
chat_service: "ChatService",
|
||||
graph: Optional[Graph] = None,
|
||||
):
|
||||
"""Build and cache the graph."""
|
||||
flow: Optional[Flow] = session.get(Flow, flow_id)
|
||||
if not flow or not flow.data:
|
||||
raise ValueError("Invalid flow ID")
|
||||
other_graph = Graph.from_payload(flow.data, flow_id)
|
||||
if graph is None:
|
||||
graph = other_graph
|
||||
else:
|
||||
graph = graph.update(other_graph)
|
||||
graph = Graph.from_payload(flow.data, flow_id)
|
||||
await chat_service.set_cache(flow_id, graph)
|
||||
return graph
|
||||
|
||||
|
||||
async def build_and_cache_graph_from_data(
|
||||
flow_id: str,
|
||||
chat_service: "ChatService",
|
||||
graph_data: dict,
|
||||
): # -> Graph | Any:
|
||||
"""Build and cache the graph."""
|
||||
graph = Graph.from_payload(graph_data, flow_id)
|
||||
await chat_service.set_cache(flow_id, graph)
|
||||
return graph
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,13 +8,15 @@ from fastapi.responses import StreamingResponse
|
|||
from loguru import logger
|
||||
|
||||
from langflow.api.utils import (
|
||||
build_and_cache_graph,
|
||||
build_and_cache_graph_from_data,
|
||||
build_and_cache_graph_from_db,
|
||||
format_elapsed_time,
|
||||
format_exception_message,
|
||||
get_top_level_vertices,
|
||||
parse_exception,
|
||||
)
|
||||
from langflow.api.v1.schemas import (
|
||||
FlowDataRequest,
|
||||
InputValueRequest,
|
||||
Log,
|
||||
ResultDataResponse,
|
||||
|
|
@ -28,7 +30,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"])
|
||||
|
|
@ -50,9 +52,10 @@ async def try_running_celery_task(vertex, user_id):
|
|||
return vertex
|
||||
|
||||
|
||||
@router.get("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||
async def get_vertices(
|
||||
flow_id: str,
|
||||
@router.post("/build/{flow_id}/vertices", response_model=VerticesOrderResponse)
|
||||
async def retrieve_vertices_order(
|
||||
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,
|
||||
chat_service: "ChatService" = Depends(get_chat_service),
|
||||
|
|
@ -63,6 +66,7 @@ async def get_vertices(
|
|||
|
||||
Args:
|
||||
flow_id (str): The ID of the flow.
|
||||
data (Optional[FlowDataRequest], optional): The flow data. Defaults to None.
|
||||
stop_component_id (str, optional): The ID of the stop component. Defaults to None.
|
||||
start_component_id (str, optional): The ID of the start component. Defaults to None.
|
||||
chat_service (ChatService, optional): The chat service dependency. Defaults to Depends(get_chat_service).
|
||||
|
|
@ -75,11 +79,15 @@ async def get_vertices(
|
|||
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
|
||||
graph = None
|
||||
if cache := await chat_service.get_cache(flow_id):
|
||||
graph = cache.get("result")
|
||||
graph = await build_and_cache_graph(flow_id, session, chat_service, graph)
|
||||
if not data:
|
||||
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_str, graph_data=data.model_dump(), chat_service=chat_service
|
||||
)
|
||||
graph.validate_stream()
|
||||
if stop_component_id or start_component_id:
|
||||
try:
|
||||
first_layer = graph.sort_vertices(stop_component_id, start_component_id)
|
||||
|
|
@ -104,6 +112,8 @@ async def get_vertices(
|
|||
return VerticesOrderResponse(ids=first_layer, run_id=run_id, vertices_to_run=vertices_to_run)
|
||||
|
||||
except Exception as exc:
|
||||
if "stream or streaming set to True" in str(exc):
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
logger.error(f"Error checking build status: {exc}")
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
|
@ -111,7 +121,7 @@ async def get_vertices(
|
|||
|
||||
@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,
|
||||
|
|
@ -136,25 +146,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}")
|
||||
graph = await build_and_cache_graph(flow_id=flow_id, session=next(get_session()), chat_service=chat_service)
|
||||
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_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,
|
||||
|
|
@ -181,7 +191,7 @@ async def build_vertex(
|
|||
|
||||
# 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_object = Log(message=log_message)
|
||||
result_data_response.logs.append(log_object)
|
||||
|
|
@ -190,7 +200,7 @@ async def build_vertex(
|
|||
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,
|
||||
logs=result_data_response.logs,
|
||||
|
|
@ -234,7 +244,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),
|
||||
|
|
@ -266,23 +276,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
|
||||
|
|
@ -20,7 +21,6 @@ from langflow.api.v1.schemas import (
|
|||
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 +54,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 +111,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=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 +153,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 +166,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 +187,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 +235,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 +274,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 +359,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 +403,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))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -33,7 +35,12 @@ def create_flow(
|
|||
flow.user_id = current_user.id
|
||||
|
||||
db_flow = Flow.model_validate(flow, from_attributes=True)
|
||||
db_flow.updated_at = datetime.utcnow()
|
||||
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()
|
||||
|
|
@ -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
|
||||
|
|
@ -128,7 +132,11 @@ def update_flow(
|
|||
for key, value in flow_data.items():
|
||||
if value is not None:
|
||||
setattr(db_flow, key, value)
|
||||
db_flow.updated_at = datetime.utcnow()
|
||||
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
|
||||
|
|
|
|||
233
src/backend/base/langflow/api/v1/folders.py
Normal file
233
src/backend/base/langflow/api/v1/folders.py
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
import orjson
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFile, status
|
||||
from sqlalchemy import update
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from langflow.api.v1.flows import create_flows
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListReadWithFolderName
|
||||
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.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
|
||||
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(Folder.user_id == current_user.id)).all()
|
||||
return folders
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/starter-projects", response_model=FolderReadWithFlows, status_code=200)
|
||||
def read_starter_folders(*, session: Session = Depends(get_session)):
|
||||
try:
|
||||
folders = session.exec(select(Folder).where(Folder.name == STARTER_FOLDER_NAME)).first()
|
||||
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,9 +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"])
|
||||
|
|
@ -39,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),
|
||||
|
|
@ -48,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))
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class BuildStatus(Enum):
|
|||
|
||||
|
||||
class TweaksRequest(BaseModel):
|
||||
tweaks: Optional[Dict[str, Dict[str, str]]] = Field(default_factory=dict)
|
||||
tweaks: Optional[Dict[str, Dict[str, Any]]] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class UpdateTemplateRequest(BaseModel):
|
||||
|
|
@ -141,10 +141,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
|
||||
|
||||
|
|
@ -299,3 +309,15 @@ class SimplifiedAPIRequest(BaseModel):
|
|||
)
|
||||
tweaks: Optional[Tweaks] = Field(default=None, description="The tweaks")
|
||||
session_id: Optional[str] = Field(default=None, description="The session id")
|
||||
|
||||
|
||||
# (alias) type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
|
||||
# nodes: Node<NodeData>[];
|
||||
# edges: Edge<EdgeData>[];
|
||||
# viewport: Viewport;
|
||||
# }
|
||||
# import ReactFlowJsonObject
|
||||
class FlowDataRequest(BaseModel):
|
||||
nodes: List[dict]
|
||||
edges: List[dict]
|
||||
viewport: Optional[dict] = None
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
|
@ -37,7 +37,11 @@ def create_variable(
|
|||
variable_dict["user_id"] = current_user.id
|
||||
|
||||
db_variable = Variable.model_validate(variable_dict)
|
||||
if not db_variable.value:
|
||||
if not db_variable.name and not db_variable.value:
|
||||
raise HTTPException(status_code=400, detail="Variable name and value cannot be empty")
|
||||
elif not db_variable.name:
|
||||
raise HTTPException(status_code=400, detail="Variable name cannot be empty")
|
||||
elif not db_variable.value:
|
||||
raise HTTPException(status_code=400, detail="Variable value cannot be empty")
|
||||
encrypted = auth_utils.encrypt_api_key(db_variable.value, settings_service=settings_service)
|
||||
db_variable.value = encrypted
|
||||
|
|
@ -85,7 +89,7 @@ def update_variable(
|
|||
variable_data = variable.model_dump(exclude_unset=True)
|
||||
for key, value in variable_data.items():
|
||||
setattr(db_variable, key, value)
|
||||
db_variable.updated_at = datetime.utcnow()
|
||||
db_variable.updated_at = datetime.now(timezone.utc)
|
||||
session.commit()
|
||||
session.refresh(db_variable)
|
||||
return db_variable
|
||||
|
|
|
|||
|
|
@ -74,23 +74,25 @@ def retrieve_file_paths(
|
|||
return file_paths
|
||||
|
||||
|
||||
def partition_file_to_record(file_path: str, silent_errors: bool) -> Optional[Record]:
|
||||
# Use the partition function to load the file
|
||||
from unstructured.partition.auto import partition # type: ignore
|
||||
# ! Removing unstructured dependency until
|
||||
# ! 3.12 is supported
|
||||
# def partition_file_to_record(file_path: str, silent_errors: bool) -> Optional[Record]:
|
||||
# # Use the partition function to load the file
|
||||
# from unstructured.partition.auto import partition # type: ignore
|
||||
|
||||
try:
|
||||
elements = partition(file_path)
|
||||
except Exception as e:
|
||||
if not silent_errors:
|
||||
raise ValueError(f"Error loading file {file_path}: {e}") from e
|
||||
return None
|
||||
# try:
|
||||
# elements = partition(file_path)
|
||||
# except Exception as e:
|
||||
# if not silent_errors:
|
||||
# raise ValueError(f"Error loading file {file_path}: {e}") from e
|
||||
# return None
|
||||
|
||||
# Create a Record
|
||||
text = "\n\n".join([Text(el) for el in elements])
|
||||
metadata = elements.metadata if hasattr(elements, "metadata") else {}
|
||||
metadata["file_path"] = file_path
|
||||
record = Record(text=text, data=metadata)
|
||||
return record
|
||||
# # Create a Record
|
||||
# text = "\n\n".join([Text(el) for el in elements])
|
||||
# metadata = elements.metadata if hasattr(elements, "metadata") else {}
|
||||
# metadata["file_path"] = file_path
|
||||
# record = Record(text=text, data=metadata)
|
||||
# return record
|
||||
|
||||
|
||||
def read_text_file(file_path: str) -> str:
|
||||
|
|
@ -138,18 +140,20 @@ def parse_text_file_to_record(file_path: str, silent_errors: bool) -> Optional[R
|
|||
return record
|
||||
|
||||
|
||||
def get_elements(
|
||||
file_paths: List[str],
|
||||
silent_errors: bool,
|
||||
max_concurrency: int,
|
||||
use_multithreading: bool,
|
||||
) -> List[Optional[Record]]:
|
||||
if use_multithreading:
|
||||
records = parallel_load_records(file_paths, silent_errors, max_concurrency)
|
||||
else:
|
||||
records = [partition_file_to_record(file_path, silent_errors) for file_path in file_paths]
|
||||
records = list(filter(None, records))
|
||||
return records
|
||||
# ! Removing unstructured dependency until
|
||||
# ! 3.12 is supported
|
||||
# def get_elements(
|
||||
# file_paths: List[str],
|
||||
# silent_errors: bool,
|
||||
# max_concurrency: int,
|
||||
# use_multithreading: bool,
|
||||
# ) -> List[Optional[Record]]:
|
||||
# if use_multithreading:
|
||||
# records = parallel_load_records(file_paths, silent_errors, max_concurrency)
|
||||
# else:
|
||||
# records = [partition_file_to_record(file_path, silent_errors) for file_path in file_paths]
|
||||
# records = list(filter(None, records))
|
||||
# return records
|
||||
|
||||
|
||||
def parallel_load_records(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import warnings
|
||||
from typing import Optional, Union
|
||||
|
||||
from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.memory import add_messages
|
||||
from langflow.memory import store_message
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
|
|
@ -58,34 +57,16 @@ class ChatComponent(CustomComponent):
|
|||
sender: Optional[str] = None,
|
||||
sender_name: Optional[str] = None,
|
||||
) -> list[Record]:
|
||||
if not message:
|
||||
warnings.warn("No message provided.")
|
||||
return []
|
||||
records = store_message(
|
||||
message,
|
||||
session_id=session_id,
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
flow_id=self.graph.flow_id,
|
||||
)
|
||||
|
||||
if not session_id or not sender or not sender_name:
|
||||
raise ValueError("All of session_id, sender, and sender_name must be provided.")
|
||||
if isinstance(message, Record):
|
||||
record = message
|
||||
record.data.update(
|
||||
{
|
||||
"session_id": session_id,
|
||||
"sender": sender,
|
||||
"sender_name": sender_name,
|
||||
}
|
||||
)
|
||||
else:
|
||||
record = Record(
|
||||
data={
|
||||
"text": message,
|
||||
"session_id": session_id,
|
||||
"sender": sender,
|
||||
"sender_name": sender_name,
|
||||
},
|
||||
)
|
||||
|
||||
self.status = record
|
||||
records = add_messages([record])
|
||||
return records[0]
|
||||
self.status = records
|
||||
return records
|
||||
|
||||
def build_with_record(
|
||||
self,
|
||||
|
|
|
|||
49
src/backend/base/langflow/base/memory/memory.py
Normal file
49
src/backend/base/langflow/base/memory/memory.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class BaseMemoryComponent(CustomComponent):
|
||||
display_name = "Chat Memory"
|
||||
description = "Retrieves stored chat messages given a specific Session ID."
|
||||
beta: bool = True
|
||||
icon = "history"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"sender": {
|
||||
"options": ["Machine", "User", "Machine and User"],
|
||||
"display_name": "Sender Type",
|
||||
},
|
||||
"sender_name": {"display_name": "Sender Name", "advanced": True},
|
||||
"n_messages": {
|
||||
"display_name": "Number of Messages",
|
||||
"info": "Number of messages to retrieve.",
|
||||
},
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"order": {
|
||||
"options": ["Ascending", "Descending"],
|
||||
"display_name": "Order",
|
||||
"info": "Order of the messages.",
|
||||
"advanced": True,
|
||||
},
|
||||
"record_template": {
|
||||
"display_name": "Record Template",
|
||||
"multiline": True,
|
||||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
raise NotImplementedError
|
||||
|
||||
def add_message(
|
||||
self, sender: str, sender_name: str, text: str, session_id: str, metadata: Optional[dict] = None, **kwargs
|
||||
):
|
||||
raise NotImplementedError
|
||||
1
src/backend/base/langflow/base/models/groq_constants.py
Normal file
1
src/backend/base/langflow/base/models/groq_constants.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
MODEL_NAMES = ["llama3-8b-8192", "llama3-70b-8192", "mixtral-8x7b-32768", "gemma-7b-it"]
|
||||
|
|
@ -57,7 +57,7 @@ class LCModelComponent(CustomComponent):
|
|||
prompt_tokens = token_usage["prompt_tokens"]
|
||||
total_tokens = token_usage["total_tokens"]
|
||||
finish_reason = response_metadata["finish_reason"]
|
||||
status_message = f"Tokens:\n- Input: {prompt_tokens}\nOutput: {completion_tokens}\nTotal Tokens: {total_tokens}\nStop Reason: {finish_reason}\nResponse: {content}"
|
||||
status_message = f"Tokens:\nInput: {prompt_tokens}\nOutput: {completion_tokens}\nTotal Tokens: {total_tokens}\nStop Reason: {finish_reason}\nResponse: {content}"
|
||||
elif all(key in response_metadata for key in anthropic_keys) and all(
|
||||
key in response_metadata["usage"] for key in inner_anthropic_keys
|
||||
):
|
||||
|
|
@ -65,7 +65,7 @@ class LCModelComponent(CustomComponent):
|
|||
input_tokens = usage["input_tokens"]
|
||||
output_tokens = usage["output_tokens"]
|
||||
stop_reason = response_metadata["stop_reason"]
|
||||
status_message = f"Tokens:\n- Input: {input_tokens}\n- Output: {output_tokens}\nStop Reason: {stop_reason}\nResponse: {content}"
|
||||
status_message = f"Tokens:\nInput: {input_tokens}\nOutput: {output_tokens}\nStop Reason: {stop_reason}\nResponse: {content}"
|
||||
else:
|
||||
status_message = f"Response: {content}"
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
MODEL_NAMES = ["gpt-4o", "gpt-4-turbo", "gpt-4-turbo-preview", "gpt-3.5-turbo", "gpt-3.5-turbo-0125"]
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ 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
|
||||
|
||||
|
||||
class ConversationalAgent(CustomComponent):
|
||||
|
|
@ -57,9 +58,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,7 +1,7 @@
|
|||
from typing import Callable, Union
|
||||
|
||||
from langchain.agents import AgentExecutor
|
||||
from langchain.sql_database import SQLDatabase
|
||||
from langchain_community.utilities import SQLDatabase
|
||||
from langchain_community.agent_toolkits import SQLDatabaseToolkit
|
||||
from langchain_community.agent_toolkits.sql.base import create_sql_agent
|
||||
|
||||
|
|
|
|||
|
|
@ -93,14 +93,14 @@ class APIRequest(CustomComponent):
|
|||
self,
|
||||
method: str,
|
||||
urls: List[str],
|
||||
_headers: Optional[Record] = None,
|
||||
headers: Optional[Record] = None,
|
||||
body: Optional[Record] = None,
|
||||
timeout: int = 5,
|
||||
) -> List[Record]:
|
||||
if _headers is None:
|
||||
headers = {}
|
||||
if headers is None:
|
||||
headers_dict = {}
|
||||
else:
|
||||
headers = _headers.data
|
||||
headers_dict = headers.data
|
||||
|
||||
bodies = []
|
||||
if body:
|
||||
|
|
@ -114,7 +114,7 @@ class APIRequest(CustomComponent):
|
|||
bodies += [None] * (len(urls) - len(bodies)) # type: ignore
|
||||
async with httpx.AsyncClient() as client:
|
||||
results = await asyncio.gather(
|
||||
*[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)]
|
||||
*[self.make_request(client, method, u, headers_dict, rec, timeout) for u, rec in zip(urls, bodies)]
|
||||
)
|
||||
self.status = results
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
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
|
||||
|
||||
|
||||
class MistralAIEmbeddingsComponent(CustomComponent):
|
||||
display_name = "MistralAI Embeddings"
|
||||
description = "Generate embeddings using MistralAI models."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Model",
|
||||
"advanced": False,
|
||||
"options": ["mistral-embed"],
|
||||
"value": "mistral-embed",
|
||||
},
|
||||
"mistral_api_key": {
|
||||
"display_name": "Mistral API Key",
|
||||
"password": True,
|
||||
"advanced": False,
|
||||
},
|
||||
"max_concurrent_requests": {
|
||||
"display_name": "Max Concurrent Requests",
|
||||
"advanced": True,
|
||||
"value": 64,
|
||||
},
|
||||
"max_retries": {
|
||||
"display_name": "Max Retries",
|
||||
"advanced": True,
|
||||
"value": 5,
|
||||
},
|
||||
"timeout": {
|
||||
"display_name": "Request Timeout",
|
||||
"advanced": True,
|
||||
"value": 120,
|
||||
},
|
||||
"endpoint": {"display_name": "API Endpoint", "advanced": True, "value": "https://api.mistral.ai/v1/"},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
mistral_api_key: str,
|
||||
model: str = "mistral-embed",
|
||||
max_concurrent_requests: int = 64,
|
||||
max_retries: int = 5,
|
||||
timeout: int = 120,
|
||||
endpoint: str = "https://api.mistral.ai/v1/",
|
||||
) -> Embeddings:
|
||||
if mistral_api_key:
|
||||
api_key = SecretStr(mistral_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
|
||||
return MistralAIEmbeddings(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
endpoint=endpoint,
|
||||
max_concurrent_requests=max_concurrent_requests,
|
||||
max_retries=max_retries,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain_community.embeddings import VertexAIEmbeddings
|
||||
from langchain_google_vertexai import VertexAIEmbeddings
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
|
|
|||
29
src/backend/base/langflow/components/experimental/Pass.py
Normal file
29
src/backend/base/langflow/components/experimental/Pass.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from typing import Union
|
||||
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": {
|
||||
"display_name": "Ignored Input",
|
||||
"info": "This input is ignored. It's used to control the flow in the graph.",
|
||||
"input_types": ["Text", "Record"],
|
||||
},
|
||||
"forwarded_input": {
|
||||
"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]:
|
||||
# The ignored_input is not used in the logic, it's just there for graph flow control
|
||||
self.status = forwarded_input
|
||||
return forwarded_input
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool
|
||||
from langchain_experimental.sql.base import SQLDatabase
|
||||
from langchain_community.utilities import SQLDatabase
|
||||
|
||||
from langflow.field_typing import Text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.field_typing import Text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.utils.util import unescape_string
|
||||
|
||||
|
||||
class SplitTextComponent(CustomComponent):
|
||||
display_name: str = "Split Text"
|
||||
description: str = "Split text into chunks of a specified length."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"inputs": {
|
||||
"display_name": "Inputs",
|
||||
"info": "Texts to split.",
|
||||
"input_types": ["Record", "Text"],
|
||||
},
|
||||
"separator": {
|
||||
"display_name": "Separator",
|
||||
"info": 'The character to split on. Defaults to " ".',
|
||||
},
|
||||
"truncate_size": {
|
||||
"display_name": "Truncate Size",
|
||||
"info": "The maximum length (in number of characters) of each chunk to keep. Defaults to 0 (no truncation).",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
inputs: list[Text],
|
||||
separator: str = " ",
|
||||
truncate_size: Optional[int] = 0,
|
||||
) -> list[Record]:
|
||||
separator = unescape_string(separator)
|
||||
|
||||
outputs = []
|
||||
for text in inputs:
|
||||
chunks = text.split(separator)
|
||||
|
||||
if truncate_size:
|
||||
chunks = [chunk[:truncate_size] for chunk in chunks]
|
||||
|
||||
for chunk in chunks:
|
||||
outputs.append(Record(text=chunk, data={"parent": text}))
|
||||
|
||||
self.status = outputs
|
||||
return outputs
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.memory import get_messages, store_message
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
class StoreMessageComponent(CustomComponent):
|
||||
display_name = "Store Message"
|
||||
description = "Stores a chat message given a Session ID."
|
||||
beta: bool = True
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"sender": {
|
||||
"options": ["Machine", "User"],
|
||||
"display_name": "Sender Type",
|
||||
},
|
||||
"sender_name": {"display_name": "Sender Name"},
|
||||
"message": {"display_name": "Message"},
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
sender: str = "User",
|
||||
sender_name: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
message: str = "",
|
||||
) -> List[Record]:
|
||||
store_message(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
message=message,
|
||||
)
|
||||
|
||||
self.status = get_messages(session_id=session_id)
|
||||
return get_messages(session_id=session_id)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
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."
|
||||
|
||||
def build_config(self) -> dict:
|
||||
return {
|
||||
"input_text": {
|
||||
"display_name": "Input Text",
|
||||
"info": "The primary text input for the operation.",
|
||||
},
|
||||
"match_text": {
|
||||
"display_name": "Match Text",
|
||||
"info": "The text input to compare against.",
|
||||
},
|
||||
"operator": {
|
||||
"display_name": "Operator",
|
||||
"info": "The operator to apply for comparing the texts.",
|
||||
"options": ["equals", "not equals", "contains", "starts with", "ends with", "exists"],
|
||||
},
|
||||
"case_sensitive": {
|
||||
"display_name": "Case Sensitive",
|
||||
"info": "If true, the comparison will be case sensitive.",
|
||||
"field_type": "bool",
|
||||
"default": False,
|
||||
},
|
||||
"true_output": {
|
||||
"display_name": "Output",
|
||||
"info": "The output to return or display when the comparison is true.",
|
||||
"input_types": ["Text", "Record"], # Allow both text and record types
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_text: Text,
|
||||
match_text: Text,
|
||||
operator: Text,
|
||||
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.")
|
||||
|
||||
if not case_sensitive:
|
||||
input_text = input_text.lower()
|
||||
match_text = match_text.lower()
|
||||
|
||||
result = False
|
||||
if operator == "equals":
|
||||
result = input_text == match_text
|
||||
elif operator == "not equals":
|
||||
result = input_text != match_text
|
||||
elif operator == "contains":
|
||||
result = match_text in input_text
|
||||
elif operator == "starts with":
|
||||
result = input_text.startswith(match_text)
|
||||
elif operator == "ends with":
|
||||
result = input_text.endswith(match_text)
|
||||
|
||||
output_record = true_output if true_output else input_text
|
||||
|
||||
if result:
|
||||
self.status = output_record
|
||||
return output_record
|
||||
else:
|
||||
self.status = "Comparison failed, stopping execution."
|
||||
self.stop()
|
||||
|
||||
return output_record
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class CombineTextsUnsortedComponent(CustomComponent):
|
||||
display_name = "Combine Texts (Unsorted)"
|
||||
description = "Concatenate text sources into a single text chunk using a specified delimiter."
|
||||
icon = "merge"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"texts": {
|
||||
"display_name": "Texts",
|
||||
"info": "The first text input to concatenate.",
|
||||
},
|
||||
"delimiter": {
|
||||
"display_name": "Delimiter",
|
||||
"info": "A string used to separate the two text inputs. Defaults to a whitespace.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, texts: list[str], delimiter: str = " ") -> Text:
|
||||
combined = delimiter.join(texts)
|
||||
self.status = combined
|
||||
return combined
|
||||
|
|
@ -23,7 +23,7 @@ class UUIDGeneratorComponent(CustomComponent):
|
|||
return {
|
||||
"unique_id": {
|
||||
"display_name": "Value",
|
||||
"real_time_refresh": True,
|
||||
"refresh_button": True,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.record import records_to_text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.memory import get_messages
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class MemoryComponent(CustomComponent):
|
||||
class MemoryComponent(BaseMemoryComponent):
|
||||
display_name = "Chat Memory"
|
||||
description = "Retrieves stored chat messages given a specific Session ID."
|
||||
beta: bool = True
|
||||
|
|
@ -42,6 +43,24 @@ class MemoryComponent(CustomComponent):
|
|||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
# Validate kwargs by checking if it contains the correct keys
|
||||
if "sender" not in kwargs:
|
||||
kwargs["sender"] = None
|
||||
if "sender_name" not in kwargs:
|
||||
kwargs["sender_name"] = None
|
||||
if "session_id" not in kwargs:
|
||||
kwargs["session_id"] = None
|
||||
if "limit" not in kwargs:
|
||||
kwargs["limit"] = 5
|
||||
if "order" not in kwargs:
|
||||
kwargs["order"] = "Descending"
|
||||
|
||||
kwargs["order"] = "DESC" if kwargs["order"] == "Descending" else "ASC"
|
||||
if kwargs["sender"] == "Machine and User":
|
||||
kwargs["sender"] = None
|
||||
return get_messages(**kwargs)
|
||||
|
||||
def build(
|
||||
self,
|
||||
sender: Optional[str] = "Machine and User",
|
||||
|
|
@ -51,10 +70,7 @@ class MemoryComponent(CustomComponent):
|
|||
order: Optional[str] = "Descending",
|
||||
record_template: Optional[str] = "{sender_name}: {text}",
|
||||
) -> Text:
|
||||
order = "DESC" if order == "Descending" else "ASC"
|
||||
if sender == "Machine and User":
|
||||
sender = None
|
||||
messages = get_messages(
|
||||
messages = self.get_messages(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
session_id=session_id,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text
|
||||
|
||||
|
||||
class ShouldRunNextComponent(CustomComponent):
|
||||
display_name = "Should Run Next"
|
||||
description = "Determines if a vertex is runnable."
|
||||
|
||||
def build(self, llm: BaseLanguageModel, question: str, context: str, retries: int = 3) -> Text:
|
||||
template = "Given the following question and the context below, answer with a yes or no.\n\n{error_message}\n\nQuestion: {question}\n\nContext: {context}\n\nAnswer:"
|
||||
|
||||
prompt = PromptTemplate.from_template(template)
|
||||
chain = prompt | llm
|
||||
error_message = ""
|
||||
for i in range(retries):
|
||||
result = chain.invoke(dict(question=question, context=context, error_message=error_message))
|
||||
if isinstance(result, BaseMessage):
|
||||
content = result.content
|
||||
elif isinstance(result, str):
|
||||
content = result
|
||||
if isinstance(content, str) and content.lower().strip() in ["yes", "no"]:
|
||||
break
|
||||
condition = str(content).lower().strip() == "yes"
|
||||
self.status = f"Should Run Next: {condition}"
|
||||
if condition is False:
|
||||
self.stop()
|
||||
return context
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
from typing import Optional, Union
|
||||
|
||||
from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.field_typing import Text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema import Record
|
||||
from langflow.utils.util import unescape_string
|
||||
|
||||
|
||||
class SplitTextComponent(CustomComponent):
|
||||
display_name: str = "Split Text"
|
||||
description: str = "Split text into chunks of a specified length."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"inputs": {
|
||||
"display_name": "Inputs",
|
||||
"info": "Texts to split.",
|
||||
"input_types": ["Record", "Text"],
|
||||
},
|
||||
"separators": {
|
||||
"display_name": "Separators",
|
||||
"info": 'The characters to split on. Defaults to [" "].',
|
||||
"is_list": True,
|
||||
},
|
||||
"chunk_size": {
|
||||
"display_name": "Max Chunk Size",
|
||||
"info": "The maximum length (in number of characters) of each chunk.",
|
||||
"field_type": "int",
|
||||
"value": 1000,
|
||||
},
|
||||
"chunk_overlap": {
|
||||
"display_name": "Chunk Overlap",
|
||||
"info": "The amount of character overlap between chunks.",
|
||||
"field_type": "int",
|
||||
"value": 200,
|
||||
},
|
||||
"recursive": {
|
||||
"display_name": "Recursive",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
inputs: list[Text],
|
||||
separators: Optional[list[str]] = [" "],
|
||||
chunk_size: Optional[int] = 1000,
|
||||
chunk_overlap: Optional[int] = 200,
|
||||
recursive: bool = False,
|
||||
) -> list[Record]:
|
||||
if separators is None:
|
||||
separators = []
|
||||
separators = [unescape_string(x) for x in separators]
|
||||
|
||||
# Make sure chunk_size and chunk_overlap are ints
|
||||
if isinstance(chunk_size, str):
|
||||
chunk_size = int(chunk_size)
|
||||
if isinstance(chunk_overlap, str):
|
||||
chunk_overlap = int(chunk_overlap)
|
||||
splitter: Optional[Union[CharacterTextSplitter, RecursiveCharacterTextSplitter]] = None
|
||||
if recursive:
|
||||
splitter = RecursiveCharacterTextSplitter(
|
||||
separators=separators,
|
||||
chunk_size=chunk_size,
|
||||
chunk_overlap=chunk_overlap,
|
||||
)
|
||||
|
||||
else:
|
||||
splitter = CharacterTextSplitter(
|
||||
separator=separators[0],
|
||||
chunk_size=chunk_size,
|
||||
chunk_overlap=chunk_overlap,
|
||||
)
|
||||
|
||||
documents = []
|
||||
for _input in inputs:
|
||||
if isinstance(_input, Record):
|
||||
documents.append(_input.to_lc_document())
|
||||
else:
|
||||
documents.append(Document(page_content=_input))
|
||||
|
||||
records = self.to_records(splitter.split_documents(documents))
|
||||
self.status = records
|
||||
return records
|
||||
|
|
@ -7,7 +7,7 @@ from langflow.schema import Record
|
|||
|
||||
class ChatInput(ChatComponent):
|
||||
display_name = "Chat Input"
|
||||
description = "Get chat inputs from the Interaction Panel."
|
||||
description = "Get chat inputs from the Playground."
|
||||
icon = "ChatInput"
|
||||
|
||||
def build_config(self):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langflow.field_typing import Text
|
|||
|
||||
class TextInput(TextComponent):
|
||||
display_name = "Text Input"
|
||||
description = "Get text inputs from the Interaction Panel."
|
||||
description = "Get text inputs from the Playground."
|
||||
icon = "type"
|
||||
|
||||
def build_config(self):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
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:
|
||||
from langchain_community.chat_message_histories.astradb import (
|
||||
AstraDBChatMessageHistory,
|
||||
)
|
||||
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_community.chat_message_histories.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:
|
||||
from langchain_community.chat_message_histories.astradb import (
|
||||
AstraDBChatMessageHistory,
|
||||
)
|
||||
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
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
from typing import Optional, cast
|
||||
|
||||
from langchain_community.chat_message_histories.zep import SearchScope, SearchType, ZepChatMessageHistory
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
||||
class ZepMessageReaderComponent(BaseMemoryComponent):
|
||||
display_name = "Zep Message Reader"
|
||||
description = "Retrieves stored chat messages from Zep."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"url": {
|
||||
"display_name": "Zep URL",
|
||||
"info": "URL of the Zep instance.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"api_key": {
|
||||
"display_name": "Zep API Key",
|
||||
"info": "API Key for the Zep instance.",
|
||||
"password": True,
|
||||
},
|
||||
"query": {
|
||||
"display_name": "Query",
|
||||
"info": "Query to search for in the chat history.",
|
||||
},
|
||||
"metadata": {
|
||||
"display_name": "Metadata",
|
||||
"info": "Optional metadata to attach to the message.",
|
||||
"advanced": True,
|
||||
},
|
||||
"search_scope": {
|
||||
"options": ["Messages", "Summary"],
|
||||
"display_name": "Search Scope",
|
||||
"info": "Scope of the search.",
|
||||
"advanced": True,
|
||||
},
|
||||
"search_type": {
|
||||
"options": ["Similarity", "MMR"],
|
||||
"display_name": "Search Type",
|
||||
"info": "Type of search.",
|
||||
"advanced": True,
|
||||
},
|
||||
"limit": {
|
||||
"display_name": "Limit",
|
||||
"info": "Limit of search results.",
|
||||
"advanced": True,
|
||||
},
|
||||
"api_base_path": {
|
||||
"display_name": "API Base Path",
|
||||
"options": ["api/v1", "api/v2"],
|
||||
},
|
||||
}
|
||||
|
||||
def get_messages(self, **kwargs) -> list[Record]:
|
||||
"""
|
||||
Retrieves messages from the ZepChatMessageHistory memory.
|
||||
|
||||
If a query is provided, the search method is used to search for messages in the memory, otherwise all messages are returned.
|
||||
|
||||
Args:
|
||||
memory (ZepChatMessageHistory): The ZepChatMessageHistory instance to retrieve messages from.
|
||||
query (str, optional): The query string to search for messages. Defaults to None.
|
||||
metadata (dict, optional): Additional metadata to filter the search results. Defaults to None.
|
||||
search_scope (str, optional): The scope of the search. Can be 'messages' or 'summary'. Defaults to 'messages'.
|
||||
search_type (str, optional): The type of search. Can be 'similarity' or 'exact'. Defaults to 'similarity'.
|
||||
limit (int, optional): The maximum number of search results to return. Defaults to None.
|
||||
|
||||
Returns:
|
||||
list[Record]: A list of Record objects representing the search results.
|
||||
"""
|
||||
memory: ZepChatMessageHistory = cast(ZepChatMessageHistory, kwargs.get("memory"))
|
||||
if not memory:
|
||||
raise ValueError("ZepChatMessageHistory instance is required.")
|
||||
query = kwargs.get("query")
|
||||
search_scope = kwargs.get("search_scope", SearchScope.messages).lower()
|
||||
search_type = kwargs.get("search_type", SearchType.similarity).lower()
|
||||
limit = kwargs.get("limit")
|
||||
|
||||
if query:
|
||||
memory_search_results = memory.search(
|
||||
query,
|
||||
search_scope=search_scope,
|
||||
search_type=search_type,
|
||||
limit=limit,
|
||||
)
|
||||
# Get the messages from the search results if the search scope is messages
|
||||
result_dicts = []
|
||||
for result in memory_search_results:
|
||||
result_dict = {}
|
||||
if search_scope == SearchScope.messages:
|
||||
result_dict["text"] = result.message
|
||||
else:
|
||||
result_dict["text"] = result.summary
|
||||
result_dict["metadata"] = result.metadata
|
||||
result_dict["score"] = result.score
|
||||
result_dicts.append(result_dict)
|
||||
results = [Record(data=result_dict) for result_dict in result_dicts]
|
||||
else:
|
||||
messages = memory.messages
|
||||
results = [Record.from_lc_message(message) for message in messages]
|
||||
return results
|
||||
|
||||
def build(
|
||||
self,
|
||||
session_id: Text,
|
||||
api_base_path: str = "api/v1",
|
||||
url: Optional[Text] = None,
|
||||
api_key: Optional[Text] = None,
|
||||
query: Optional[Text] = None,
|
||||
search_scope: SearchScope = SearchScope.messages,
|
||||
search_type: SearchType = SearchType.similarity,
|
||||
limit: Optional[int] = None,
|
||||
) -> list[Record]:
|
||||
try:
|
||||
from zep_python import ZepClient
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
|
||||
# Monkeypatch API_BASE_PATH to
|
||||
# avoid 404
|
||||
# This is a workaround for the local Zep instance
|
||||
# cloud Zep works with v2
|
||||
import zep_python.zep_client
|
||||
|
||||
zep_python.zep_client.API_BASE_PATH = api_base_path
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import zep-python package. " "Please install it with `pip install zep-python`."
|
||||
)
|
||||
if url == "":
|
||||
url = None
|
||||
|
||||
zep_client = ZepClient(api_url=url, api_key=api_key)
|
||||
memory = ZepChatMessageHistory(session_id=session_id, zep_client=zep_client)
|
||||
records = self.get_messages(
|
||||
memory=memory,
|
||||
query=query,
|
||||
search_scope=search_scope,
|
||||
search_type=search_type,
|
||||
limit=limit,
|
||||
)
|
||||
self.status = records
|
||||
return records
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from langflow.base.memory.memory import BaseMemoryComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
|
||||
|
||||
class ZepMessageWriterComponent(BaseMemoryComponent):
|
||||
display_name = "Zep Message Writer"
|
||||
description = "Writes a message to Zep."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"session_id": {
|
||||
"display_name": "Session ID",
|
||||
"info": "Session ID of the chat history.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"url": {
|
||||
"display_name": "Zep URL",
|
||||
"info": "URL of the Zep instance.",
|
||||
"input_types": ["Text"],
|
||||
},
|
||||
"api_key": {
|
||||
"display_name": "Zep API Key",
|
||||
"info": "API Key for the Zep instance.",
|
||||
"password": True,
|
||||
},
|
||||
"limit": {
|
||||
"display_name": "Limit",
|
||||
"info": "Limit of search results.",
|
||||
"advanced": True,
|
||||
},
|
||||
"input_value": {
|
||||
"display_name": "Input Record",
|
||||
"info": "Record to write to Zep.",
|
||||
},
|
||||
"api_base_path": {
|
||||
"display_name": "API Base Path",
|
||||
"options": ["api/v1", "api/v2"],
|
||||
},
|
||||
}
|
||||
|
||||
def add_message(
|
||||
self, sender: Text, sender_name: Text, text: Text, session_id: Text, metadata: dict | None = None, **kwargs
|
||||
):
|
||||
"""
|
||||
Adds a message to the ZepChatMessageHistory 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 ZepChatMessageHistory instance is not provided.
|
||||
|
||||
"""
|
||||
memory: ZepChatMessageHistory | None = kwargs.pop("memory", None)
|
||||
if memory is None:
|
||||
raise ValueError("ZepChatMessageHistory instance is required.")
|
||||
if metadata is None:
|
||||
metadata = {}
|
||||
metadata["sender_name"] = sender_name
|
||||
metadata.update(kwargs)
|
||||
if sender == "Machine":
|
||||
memory.add_ai_message(text, metadata=metadata)
|
||||
elif sender == "User":
|
||||
memory.add_user_message(text, metadata=metadata)
|
||||
else:
|
||||
raise ValueError(f"Invalid sender type: {sender}")
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_value: Record,
|
||||
session_id: Text,
|
||||
api_base_path: str = "api/v1",
|
||||
url: Optional[Text] = None,
|
||||
api_key: Optional[Text] = None,
|
||||
) -> Record:
|
||||
try:
|
||||
# Monkeypatch API_BASE_PATH to
|
||||
# avoid 404
|
||||
# This is a workaround for the local Zep instance
|
||||
# cloud Zep works with v2
|
||||
import zep_python.zep_client
|
||||
from zep_python import ZepClient
|
||||
from zep_python.langchain import ZepChatMessageHistory
|
||||
|
||||
zep_python.zep_client.API_BASE_PATH = api_base_path
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import zep-python package. " "Please install it with `pip install zep-python`."
|
||||
)
|
||||
if url == "":
|
||||
url = None
|
||||
|
||||
zep_client = ZepClient(api_url=url, api_key=api_key)
|
||||
memory = ZepChatMessageHistory(session_id=session_id, zep_client=zep_client)
|
||||
self.add_message(**input_value.data, memory=memory)
|
||||
self.status = f"Added message to Zep memory for session {session_id}"
|
||||
return input_value
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.baidu_qianfan_endpoint import QianfanLLMEndpoint
|
||||
from langchain_community.llms.baidu_qianfan_endpoint import QianfanLLMEndpoint
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_mistralai import ChatMistralAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
|
||||
|
||||
class MistralAIModelComponent(CustomComponent):
|
||||
display_name: str = "MistralAI"
|
||||
description: str = "Generate text using MistralAI LLMs."
|
||||
icon = "MistralAI"
|
||||
|
||||
field_order = [
|
||||
"model",
|
||||
"mistral_api_key",
|
||||
"max_tokens",
|
||||
"temperature",
|
||||
"mistral_api_base",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Model Name",
|
||||
"options": [
|
||||
"open-mistral-7b",
|
||||
"open-mixtral-8x7b",
|
||||
"open-mixtral-8x22b",
|
||||
"mistral-small-latest",
|
||||
"mistral-medium-latest",
|
||||
"mistral-large-latest",
|
||||
],
|
||||
"info": "Name of the model to use.",
|
||||
"required": True,
|
||||
"value": "open-mistral-7b",
|
||||
},
|
||||
"mistral_api_key": {
|
||||
"display_name": "Mistral API Key",
|
||||
"required": True,
|
||||
"password": True,
|
||||
"info": "Your Mistral API key.",
|
||||
},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Tokens",
|
||||
"field_type": "int",
|
||||
"advanced": True,
|
||||
"value": 256,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"field_type": "float",
|
||||
"value": 0.1,
|
||||
},
|
||||
"mistral_api_base": {
|
||||
"display_name": "Mistral API Base",
|
||||
"advanced": True,
|
||||
"info": "Endpoint of the Mistral API. Defaults to 'https://api.mistral.ai' if not specified.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
model: str,
|
||||
temperature: float = 0.1,
|
||||
mistral_api_key: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
mistral_api_base: Optional[str] = None,
|
||||
) -> BaseLanguageModel:
|
||||
# Set default API endpoint if not provided
|
||||
if not mistral_api_base:
|
||||
mistral_api_base = "https://api.mistral.ai"
|
||||
|
||||
try:
|
||||
output = ChatMistralAI(
|
||||
model_name=model,
|
||||
api_key=(SecretStr(mistral_api_key) if mistral_api_key else None),
|
||||
max_tokens=max_tokens,
|
||||
temperature=temperature,
|
||||
endpoint=mistral_api_base,
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to Mistral API.") from e
|
||||
|
||||
return output
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain_community.chat_models.openai import ChatOpenAI
|
||||
from langchain_openai import ChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.field_typing import NestedDict
|
||||
|
||||
from langflow.base.models.openai_constants import MODEL_NAMES
|
||||
from langflow.field_typing import BaseLanguageModel, NestedDict
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
||||
|
|
@ -24,19 +26,7 @@ class ChatOpenAIComponent(CustomComponent):
|
|||
"advanced": True,
|
||||
"required": False,
|
||||
},
|
||||
"model_name": {
|
||||
"display_name": "Model Name",
|
||||
"advanced": False,
|
||||
"required": False,
|
||||
"options": [
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106",
|
||||
],
|
||||
},
|
||||
"model_name": {"display_name": "Model Name", "advanced": False, "options": MODEL_NAMES},
|
||||
"openai_api_base": {
|
||||
"display_name": "OpenAI API Base",
|
||||
"advanced": False,
|
||||
|
|
@ -64,18 +54,22 @@ class ChatOpenAIComponent(CustomComponent):
|
|||
self,
|
||||
max_tokens: Optional[int] = 256,
|
||||
model_kwargs: NestedDict = {},
|
||||
model_name: str = "gpt-4-1106-preview",
|
||||
model_name: str = "gpt-4o",
|
||||
openai_api_base: Optional[str] = None,
|
||||
openai_api_key: Optional[str] = None,
|
||||
temperature: float = 0.7,
|
||||
) -> 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,86 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_groq import ChatGroq
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.groq_constants import MODEL_NAMES
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
|
||||
|
||||
class GroqModelSpecs(LCModelComponent):
|
||||
display_name: str = "Groq"
|
||||
description: str = "Generate text using Groq."
|
||||
icon = "Groq"
|
||||
|
||||
field_order = [
|
||||
"groq_api_key",
|
||||
"model",
|
||||
"max_output_tokens",
|
||||
"temperature",
|
||||
"top_k",
|
||||
"top_p",
|
||||
"n",
|
||||
"input_value",
|
||||
"system_message",
|
||||
"stream",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"groq_api_key": {
|
||||
"display_name": "Groq API Key",
|
||||
"info": "API key for the Groq API.",
|
||||
"password": True,
|
||||
},
|
||||
"groq_api_base": {
|
||||
"display_name": "Groq API Base",
|
||||
"info": "Base URL path for API requests, leave blank if not using a proxy or service emulator.",
|
||||
"advanced": True,
|
||||
},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Output Tokens",
|
||||
"info": "The maximum number of tokens to generate.",
|
||||
"advanced": True,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"info": "Run inference with this temperature. Must by in the closed interval [0.0, 1.0].",
|
||||
},
|
||||
"n": {
|
||||
"display_name": "N",
|
||||
"info": "Number of chat completions to generate for each prompt. Note that the API may not return the full n completions if duplicates are generated.",
|
||||
"advanced": True,
|
||||
},
|
||||
"model_name": {
|
||||
"display_name": "Model",
|
||||
"info": "The name of the model to use. Supported examples: gemini-pro",
|
||||
"options": MODEL_NAMES,
|
||||
},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
groq_api_key: str,
|
||||
model_name: str,
|
||||
groq_api_base: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
temperature: float = 0.1,
|
||||
n: Optional[int] = 1,
|
||||
stream: bool = False,
|
||||
) -> BaseLanguageModel:
|
||||
return ChatGroq(
|
||||
model_name=model_name,
|
||||
max_tokens=max_tokens or None, # type: ignore
|
||||
temperature=temperature,
|
||||
groq_api_base=groq_api_base,
|
||||
n=n or 1,
|
||||
groq_api_key=SecretStr(groq_api_key),
|
||||
streaming=stream,
|
||||
)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Optional
|
||||
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain.llms.huggingface_endpoint import HuggingFaceEndpoint
|
||||
from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLanguageModel
|
||||
from langchain_openai import AzureChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain_community.chat_models.litellm import ChatLiteLLM, ChatLiteLLMExce
|
|||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class ChatLiteLLMModelComponent(LCModelComponent):
|
||||
|
|
|
|||
95
src/backend/base/langflow/components/models/GroqModel.py
Normal file
95
src/backend/base/langflow/components/models/GroqModel.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_groq import ChatGroq
|
||||
from langflow.base.models.groq_constants import MODEL_NAMES
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class GroqModel(LCModelComponent):
|
||||
display_name: str = "Groq"
|
||||
description: str = "Generate text using Groq."
|
||||
icon = "Groq"
|
||||
|
||||
field_order = [
|
||||
"groq_api_key",
|
||||
"model",
|
||||
"max_output_tokens",
|
||||
"temperature",
|
||||
"top_k",
|
||||
"top_p",
|
||||
"n",
|
||||
"input_value",
|
||||
"system_message",
|
||||
"stream",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"groq_api_key": {
|
||||
"display_name": "Groq API Key",
|
||||
"info": "API key for the Groq API.",
|
||||
"password": True,
|
||||
},
|
||||
"groq_api_base": {
|
||||
"display_name": "Groq API Base",
|
||||
"info": "Base URL path for API requests, leave blank if not using a proxy or service emulator.",
|
||||
"advanced": True,
|
||||
},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Output Tokens",
|
||||
"info": "The maximum number of tokens to generate.",
|
||||
"advanced": True,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"info": "Run inference with this temperature. Must by in the closed interval [0.0, 1.0].",
|
||||
},
|
||||
"n": {
|
||||
"display_name": "N",
|
||||
"info": "Number of chat completions to generate for each prompt. Note that the API may not return the full n completions if duplicates are generated.",
|
||||
"advanced": True,
|
||||
},
|
||||
"model_name": {
|
||||
"display_name": "Model",
|
||||
"info": "The name of the model to use. Supported examples: gemini-pro",
|
||||
"options": MODEL_NAMES,
|
||||
},
|
||||
"input_value": {"display_name": "Input", "info": "The input to the model."},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
"advanced": True,
|
||||
},
|
||||
"system_message": {
|
||||
"display_name": "System Message",
|
||||
"info": "System message to pass to the model.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
groq_api_key: str,
|
||||
model_name: str,
|
||||
input_value: Text,
|
||||
groq_api_base: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
temperature: float = 0.1,
|
||||
n: Optional[int] = 1,
|
||||
stream: bool = False,
|
||||
system_message: Optional[str] = None,
|
||||
) -> Text:
|
||||
output = ChatGroq(
|
||||
model_name=model_name,
|
||||
max_tokens=max_tokens or None, # type: ignore
|
||||
temperature=temperature,
|
||||
groq_api_base=groq_api_base,
|
||||
n=n or 1,
|
||||
groq_api_key=SecretStr(groq_api_key),
|
||||
streaming=stream,
|
||||
)
|
||||
return self.get_chat_result(output, stream, input_value, system_message)
|
||||
141
src/backend/base/langflow/components/models/MistralModel.py
Normal file
141
src/backend/base/langflow/components/models/MistralModel.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_mistralai import ChatMistralAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class MistralAIModelComponent(LCModelComponent):
|
||||
display_name = "MistralAI"
|
||||
description = "Generates text using MistralAI LLMs."
|
||||
icon = "MistralAI"
|
||||
|
||||
field_order = [
|
||||
"max_tokens",
|
||||
"model_kwargs",
|
||||
"model_name",
|
||||
"mistral_api_base",
|
||||
"mistral_api_key",
|
||||
"temperature",
|
||||
"input_value",
|
||||
"system_message",
|
||||
"stream",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"input_value": {"display_name": "Input"},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Tokens",
|
||||
"advanced": True,
|
||||
},
|
||||
"model_name": {
|
||||
"display_name": "Model Name",
|
||||
"advanced": False,
|
||||
"options": [
|
||||
"open-mistral-7b",
|
||||
"open-mixtral-8x7b",
|
||||
"open-mixtral-8x22b",
|
||||
"mistral-small-latest",
|
||||
"mistral-medium-latest",
|
||||
"mistral-large-latest",
|
||||
],
|
||||
"value": "open-mistral-7b",
|
||||
},
|
||||
"mistral_api_base": {
|
||||
"display_name": "Mistral API Base",
|
||||
"advanced": True,
|
||||
"info": (
|
||||
"The base URL of the Mistral API. Defaults to https://api.mistral.ai.\n\n"
|
||||
"You can change this to use other APIs like JinaChat, LocalAI and Prem."
|
||||
),
|
||||
},
|
||||
"mistral_api_key": {
|
||||
"display_name": "Mistral API Key",
|
||||
"info": "The Mistral API Key to use for the Mistral model.",
|
||||
"advanced": False,
|
||||
"password": True,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"advanced": False,
|
||||
"value": 0.1,
|
||||
},
|
||||
"stream": {
|
||||
"display_name": "Stream",
|
||||
"info": STREAM_INFO_TEXT,
|
||||
"advanced": True,
|
||||
},
|
||||
"system_message": {
|
||||
"display_name": "System Message",
|
||||
"info": "System message to pass to the model.",
|
||||
"advanced": True,
|
||||
},
|
||||
"max_retries": {
|
||||
"display_name": "Max Retries",
|
||||
"advanced": True,
|
||||
},
|
||||
"timeout": {
|
||||
"display_name": "Timeout",
|
||||
"advanced": True,
|
||||
},
|
||||
"max_concurrent_requests": {
|
||||
"display_name": "Max Concurrent Requests",
|
||||
"advanced": True,
|
||||
},
|
||||
"top_p": {
|
||||
"display_name": "Top P",
|
||||
"advanced": True,
|
||||
},
|
||||
"random_seed": {
|
||||
"display_name": "Random Seed",
|
||||
"advanced": True,
|
||||
},
|
||||
"safe_mode": {
|
||||
"display_name": "Safe Mode",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
input_value: Text,
|
||||
mistral_api_key: str,
|
||||
model_name: str,
|
||||
temperature: float = 0.1,
|
||||
max_tokens: Optional[int] = 256,
|
||||
mistral_api_base: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
system_message: Optional[str] = None,
|
||||
max_retries: int = 5,
|
||||
timeout: int = 120,
|
||||
max_concurrent_requests: int = 64,
|
||||
top_p: float = 1,
|
||||
random_seed: Optional[int] = None,
|
||||
safe_mode: bool = False,
|
||||
) -> Text:
|
||||
if not mistral_api_base:
|
||||
mistral_api_base = "https://api.mistral.ai"
|
||||
if mistral_api_key:
|
||||
api_key = SecretStr(mistral_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
|
||||
chat_model = ChatMistralAI(
|
||||
max_tokens=max_tokens,
|
||||
model_name=model_name,
|
||||
endpoint=mistral_api_base,
|
||||
api_key=api_key,
|
||||
temperature=temperature,
|
||||
max_retries=max_retries,
|
||||
timeout=timeout,
|
||||
max_concurrent_requests=max_concurrent_requests,
|
||||
top_p=top_p,
|
||||
random_seed=random_seed,
|
||||
safe_mode=safe_mode,
|
||||
)
|
||||
|
||||
return self.get_chat_result(chat_model, stream, input_value, system_message)
|
||||
|
|
@ -5,6 +5,7 @@ from pydantic.v1 import SecretStr
|
|||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.base.models.openai_constants import MODEL_NAMES
|
||||
from langflow.field_typing import NestedDict, Text
|
||||
|
||||
|
||||
|
|
@ -39,17 +40,7 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
"model_name": {
|
||||
"display_name": "Model Name",
|
||||
"advanced": False,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106",
|
||||
],
|
||||
"value": "gpt-4-turbo-preview",
|
||||
"options": MODEL_NAMES,
|
||||
},
|
||||
"openai_api_base": {
|
||||
"display_name": "OpenAI API Base",
|
||||
|
|
@ -87,7 +78,7 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
input_value: Text,
|
||||
openai_api_key: str,
|
||||
temperature: float,
|
||||
model_name: str,
|
||||
model_name: str = "gpt-4o",
|
||||
max_tokens: Optional[int] = 256,
|
||||
model_kwargs: NestedDict = {},
|
||||
openai_api_base: Optional[str] = None,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from langflow.schema import Record
|
|||
|
||||
class ChatOutput(ChatComponent):
|
||||
display_name = "Chat Output"
|
||||
description = "Display a chat message in the Interaction Panel."
|
||||
description = "Display a chat message in the Playground."
|
||||
icon = "ChatOutput"
|
||||
|
||||
def build(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -6,7 +6,7 @@ from langflow.field_typing import Text
|
|||
|
||||
class TextOutput(TextComponent):
|
||||
display_name = "Text Output"
|
||||
description = "Display a text output in the Interaction Panel."
|
||||
description = "Display a text output in the Playground."
|
||||
icon = "type"
|
||||
|
||||
def build_config(self):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Couchbase import CouchbaseComponent
|
||||
from langflow.field_typing import Embeddings, NestedDict, 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,5 +1,7 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain_pinecone._utilities import DistanceStrategy
|
||||
|
||||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Pinecone import PineconeComponent
|
||||
from langflow.field_typing import Embeddings, Text
|
||||
|
|
@ -11,8 +13,11 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
display_name = "Pinecone Search"
|
||||
description = "Search a Pinecone Vector Store for similar documents."
|
||||
icon = "Pinecone"
|
||||
field_order = ["index_name", "namespace", "distance_strategy", "pinecone_api_key", "input_value", "embedding"]
|
||||
|
||||
def build_config(self):
|
||||
distance_options = [e.value.title().replace("_", " ") for e in DistanceStrategy]
|
||||
distance_value = distance_options[0]
|
||||
return {
|
||||
"search_type": {
|
||||
"display_name": "Search Type",
|
||||
|
|
@ -21,17 +26,19 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
"input_value": {"display_name": "Input"},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"index_name": {"display_name": "Index Name"},
|
||||
"namespace": {"display_name": "Namespace"},
|
||||
"namespace": {"display_name": "Namespace", "advanced": True},
|
||||
"distance_strategy": {
|
||||
"display_name": "Distance Strategy",
|
||||
# get values from enum
|
||||
# and make them title case for display
|
||||
"options": distance_options,
|
||||
"advanced": True,
|
||||
"value": distance_value,
|
||||
},
|
||||
"pinecone_api_key": {
|
||||
"display_name": "Pinecone API Key",
|
||||
"default": "",
|
||||
"password": True,
|
||||
"required": True,
|
||||
},
|
||||
"pinecone_env": {
|
||||
"display_name": "Pinecone Environment",
|
||||
"default": "",
|
||||
"required": True,
|
||||
},
|
||||
"pool_threads": {
|
||||
"display_name": "Pool Threads",
|
||||
|
|
@ -43,13 +50,18 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
"info": "Number of results to return.",
|
||||
"advanced": True,
|
||||
},
|
||||
"text_key": {
|
||||
"display_name": "Text Key",
|
||||
"info": "Key in the record to use as text.",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build( # type: ignore[override]
|
||||
self,
|
||||
input_value: Text,
|
||||
embedding: Embeddings,
|
||||
pinecone_env: str,
|
||||
distance_strategy: str,
|
||||
text_key: str = "text",
|
||||
number_of_results: int = 4,
|
||||
pool_threads: int = 4,
|
||||
|
|
@ -61,7 +73,7 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
) -> List[Record]: # type: ignore[override]
|
||||
vector_store = super().build(
|
||||
embedding=embedding,
|
||||
pinecone_env=pinecone_env,
|
||||
distance_strategy=distance_strategy,
|
||||
inputs=[],
|
||||
text_key=text_key,
|
||||
pool_threads=pool_threads,
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreCompo
|
|||
input_value: Text,
|
||||
search_type: str,
|
||||
url: str,
|
||||
index_name: str,
|
||||
number_of_results: int = 4,
|
||||
search_by_text: bool = False,
|
||||
api_key: Optional[str] = None,
|
||||
index_name: Optional[str] = None,
|
||||
text_key: str = "text",
|
||||
embedding: Optional[Embeddings] = None,
|
||||
attributes: Optional[list] = None,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langchain.schema import BaseRetriever
|
||||
|
||||
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
|
||||
|
||||
|
||||
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,10 +1,10 @@
|
|||
import os
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import pinecone # type: ignore
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.pinecone import Pinecone
|
||||
from langchain_core.documents import Document
|
||||
from langchain_pinecone._utilities import DistanceStrategy
|
||||
from langchain_pinecone.vectorstores import PineconeVectorStore
|
||||
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
@ -15,24 +15,31 @@ class PineconeComponent(CustomComponent):
|
|||
display_name = "Pinecone"
|
||||
description = "Construct Pinecone wrapper from raw documents."
|
||||
icon = "Pinecone"
|
||||
field_order = ["index_name", "namespace", "distance_strategy", "pinecone_api_key", "documents", "embedding"]
|
||||
|
||||
def build_config(self):
|
||||
distance_options = [e.value.title().replace("_", " ") for e in DistanceStrategy]
|
||||
distance_value = distance_options[0]
|
||||
return {
|
||||
"inputs": {"display_name": "Input", "input_types": ["Document", "Record"]},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"index_name": {"display_name": "Index Name"},
|
||||
"namespace": {"display_name": "Namespace"},
|
||||
"text_key": {"display_name": "Text Key"},
|
||||
"distance_strategy": {
|
||||
"display_name": "Distance Strategy",
|
||||
# get values from enum
|
||||
# and make them title case for display
|
||||
"options": distance_options,
|
||||
"advanced": True,
|
||||
"value": distance_value,
|
||||
},
|
||||
"pinecone_api_key": {
|
||||
"display_name": "Pinecone API Key",
|
||||
"default": "",
|
||||
"password": True,
|
||||
"required": True,
|
||||
},
|
||||
"pinecone_env": {
|
||||
"display_name": "Pinecone Environment",
|
||||
"default": "",
|
||||
"required": True,
|
||||
},
|
||||
"pool_threads": {
|
||||
"display_name": "Pool Threads",
|
||||
"default": 1,
|
||||
|
|
@ -40,23 +47,79 @@ class PineconeComponent(CustomComponent):
|
|||
},
|
||||
}
|
||||
|
||||
def from_existing_index(
|
||||
self,
|
||||
index_name: str,
|
||||
embedding: Embeddings,
|
||||
pinecone_api_key: str | None,
|
||||
text_key: str = "text",
|
||||
namespace: Optional[str] = None,
|
||||
distance_strategy: DistanceStrategy = DistanceStrategy.COSINE,
|
||||
pool_threads: int = 4,
|
||||
) -> PineconeVectorStore:
|
||||
"""Load pinecone vectorstore from index name."""
|
||||
pinecone_index = PineconeVectorStore.get_pinecone_index(
|
||||
index_name, pool_threads, pinecone_api_key=pinecone_api_key
|
||||
)
|
||||
return PineconeVectorStore(
|
||||
index=pinecone_index,
|
||||
embedding=embedding,
|
||||
text_key=text_key,
|
||||
namespace=namespace,
|
||||
distance_strategy=distance_strategy,
|
||||
)
|
||||
|
||||
def from_documents(
|
||||
self,
|
||||
documents: List[Document],
|
||||
embedding: Embeddings,
|
||||
index_name: str,
|
||||
pinecone_api_key: str | None,
|
||||
text_key: str = "text",
|
||||
namespace: Optional[str] = None,
|
||||
pool_threads: int = 4,
|
||||
distance_strategy: DistanceStrategy = DistanceStrategy.COSINE,
|
||||
batch_size: int = 32,
|
||||
upsert_kwargs: Optional[dict] = None,
|
||||
embeddings_chunk_size: int = 1000,
|
||||
) -> PineconeVectorStore:
|
||||
"""Create a new pinecone vectorstore from documents."""
|
||||
texts = [d.page_content for d in documents]
|
||||
metadatas = [d.metadata for d in documents]
|
||||
pinecone = self.from_existing_index(
|
||||
index_name=index_name,
|
||||
embedding=embedding,
|
||||
pinecone_api_key=pinecone_api_key,
|
||||
text_key=text_key,
|
||||
namespace=namespace,
|
||||
distance_strategy=distance_strategy,
|
||||
pool_threads=pool_threads,
|
||||
)
|
||||
pinecone.add_texts(
|
||||
texts,
|
||||
metadatas=metadatas,
|
||||
ids=None,
|
||||
namespace=namespace,
|
||||
batch_size=batch_size,
|
||||
embedding_chunk_size=embeddings_chunk_size,
|
||||
**(upsert_kwargs or {}),
|
||||
)
|
||||
return pinecone
|
||||
|
||||
def build(
|
||||
self,
|
||||
embedding: Embeddings,
|
||||
pinecone_env: str,
|
||||
distance_strategy: str,
|
||||
inputs: Optional[List[Record]] = None,
|
||||
text_key: str = "text",
|
||||
pool_threads: int = 4,
|
||||
index_name: Optional[str] = None,
|
||||
pinecone_api_key: Optional[str] = None,
|
||||
namespace: Optional[str] = "default",
|
||||
) -> Union[VectorStore, Pinecone, BaseRetriever]:
|
||||
if pinecone_api_key is None or pinecone_env is None:
|
||||
raise ValueError("Pinecone API Key and Environment are required.")
|
||||
if os.getenv("PINECONE_API_KEY") is None and pinecone_api_key is None:
|
||||
raise ValueError("Pinecone API Key is required.")
|
||||
|
||||
pinecone.init(api_key=pinecone_api_key, environment=pinecone_env) # type: ignore
|
||||
) -> Union[VectorStore, BaseRetriever]:
|
||||
# get distance strategy from string
|
||||
distance_strategy = distance_strategy.replace(" ", "_").upper()
|
||||
_distance_strategy = DistanceStrategy[distance_strategy]
|
||||
if not index_name:
|
||||
raise ValueError("Index Name is required.")
|
||||
documents = []
|
||||
|
|
@ -66,19 +129,23 @@ class PineconeComponent(CustomComponent):
|
|||
else:
|
||||
documents.append(_input)
|
||||
if documents:
|
||||
return Pinecone.from_documents(
|
||||
return self.from_documents(
|
||||
documents=documents,
|
||||
embedding=embedding,
|
||||
index_name=index_name,
|
||||
pool_threads=pool_threads,
|
||||
namespace=namespace,
|
||||
pinecone_api_key=pinecone_api_key,
|
||||
text_key=text_key,
|
||||
namespace=namespace,
|
||||
distance_strategy=_distance_strategy,
|
||||
pool_threads=pool_threads,
|
||||
)
|
||||
|
||||
return Pinecone.from_existing_index(
|
||||
return self.from_existing_index(
|
||||
index_name=index_name,
|
||||
embedding=embedding,
|
||||
pinecone_api_key=pinecone_api_key,
|
||||
text_key=text_key,
|
||||
namespace=namespace,
|
||||
distance_strategy=_distance_strategy,
|
||||
pool_threads=pool_threads,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import weaviate # type: ignore
|
|||
from langchain.embeddings.base import Embeddings
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain_community.vectorstores import VectorStore, Weaviate
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
|
@ -50,9 +51,9 @@ class WeaviateVectorStoreComponent(CustomComponent):
|
|||
def build(
|
||||
self,
|
||||
url: str,
|
||||
index_name: str,
|
||||
search_by_text: bool = False,
|
||||
api_key: Optional[str] = None,
|
||||
index_name: Optional[str] = None,
|
||||
text_key: str = "text",
|
||||
embedding: Optional[Embeddings] = None,
|
||||
inputs: Optional[Record] = None,
|
||||
|
|
@ -78,11 +79,13 @@ class WeaviateVectorStoreComponent(CustomComponent):
|
|||
return pascal_case_word
|
||||
|
||||
index_name = _to_pascal_case(index_name) if index_name else None
|
||||
documents = []
|
||||
if not index_name:
|
||||
raise ValueError("Index name is required")
|
||||
documents: list[Document] = []
|
||||
for _input in inputs or []:
|
||||
if isinstance(_input, Record):
|
||||
documents.append(_input.to_lc_document())
|
||||
else:
|
||||
elif isinstance(_input, Document):
|
||||
documents.append(_input)
|
||||
|
||||
if documents and embedding is not None:
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -224,11 +224,7 @@ wrappers:
|
|||
documentation: ""
|
||||
SQLDatabase:
|
||||
documentation: ""
|
||||
output_parsers:
|
||||
StructuredOutputParser:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/output_parsers/structured"
|
||||
ResponseSchema:
|
||||
documentation: "https://python.langchain.com/docs/modules/model_io/output_parsers/structured"
|
||||
custom_components:
|
||||
CustomComponent:
|
||||
documentation: "https://docs.langflow.org/guidelines/custom-component"
|
||||
# documentation: "https://docs.langflow.org/administration/custom-component"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -3,7 +3,7 @@ import uuid
|
|||
from collections import defaultdict, deque
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Callable, Coroutine, Dict, Generator, List, Optional, Type, Union
|
||||
from typing import TYPE_CHECKING, Callable, Coroutine, Dict, Generator, List, Optional, Tuple, Type, Union
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
|
@ -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, RoutingVertex, 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
|
||||
|
|
@ -75,7 +75,7 @@ class Graph:
|
|||
self.vertices: List[Vertex] = []
|
||||
self.run_manager = RunnableVerticesManager()
|
||||
self._build_graph()
|
||||
self.build_graph_maps()
|
||||
self.build_graph_maps(self.edges)
|
||||
self.define_vertices_lists()
|
||||
self.state_manager = GraphStateManager()
|
||||
|
||||
|
|
@ -130,6 +130,18 @@ class Graph:
|
|||
):
|
||||
vertices_ids.append(vertex_id)
|
||||
successors = self.get_all_successors(vertex, flat=True)
|
||||
# Update run_manager.run_predecessors because we are activating vertices
|
||||
# The run_prdecessors is the predecessor map of the vertices
|
||||
# we remove the vertex_id from the predecessor map whenever we run a vertex
|
||||
# So we need to get all edges of the vertex and successors
|
||||
# and run self.build_adjacency_maps(edges) to get the new predecessor map
|
||||
# that is not complete but we can use to update the run_predecessors
|
||||
edges_set = set()
|
||||
for vertex in [vertex] + successors:
|
||||
edges_set.update(vertex.edges)
|
||||
edges = list(edges_set)
|
||||
new_predecessor_map, _ = self.build_adjacency_maps(edges)
|
||||
self.run_manager.run_predecessors.update(new_predecessor_map)
|
||||
self.vertices_to_run.update(list(map(lambda x: x.id, successors)))
|
||||
self.activated_vertices = vertices_ids
|
||||
self.vertices_to_run.update(vertices_ids)
|
||||
|
|
@ -154,6 +166,25 @@ class Graph:
|
|||
|
||||
self.state_manager.append_state(name, record, run_id=self._run_id)
|
||||
|
||||
def validate_stream(self):
|
||||
"""
|
||||
Validates the stream configuration of the graph.
|
||||
|
||||
If there are two vertices in the same graph (connected by edges)
|
||||
that have `stream=True` or `streaming=True`, raises a `ValueError`.
|
||||
|
||||
Raises:
|
||||
ValueError: If two connected vertices have `stream=True` or `streaming=True`.
|
||||
"""
|
||||
for vertex in self.vertices:
|
||||
if vertex.params.get("stream") or vertex.params.get("streaming"):
|
||||
successors = self.get_all_successors(vertex)
|
||||
for successor in successors:
|
||||
if successor.params.get("stream") or successor.params.get("streaming"):
|
||||
raise ValueError(
|
||||
f"Components {vertex.display_name} and {successor.display_name} are connected and both have stream or streaming set to True"
|
||||
)
|
||||
|
||||
@property
|
||||
def run_id(self):
|
||||
"""
|
||||
|
|
@ -211,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.
|
||||
|
|
@ -258,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)
|
||||
|
|
@ -284,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.
|
||||
|
|
@ -309,6 +342,7 @@ class Graph:
|
|||
outputs=outputs,
|
||||
session_id=session_id,
|
||||
stream=stream,
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
@ -331,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.
|
||||
|
|
@ -372,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}")
|
||||
|
|
@ -401,14 +437,20 @@ class Graph:
|
|||
"inactivated_vertices": self.inactivated_vertices,
|
||||
}
|
||||
|
||||
def build_graph_maps(self):
|
||||
def build_graph_maps(self, edges: Optional[List[ContractEdge]] = None, vertices: Optional[List[Vertex]] = None):
|
||||
"""
|
||||
Builds the adjacency maps for the graph.
|
||||
"""
|
||||
self.predecessor_map, self.successor_map = self.build_adjacency_maps()
|
||||
if edges is None:
|
||||
edges = self.edges
|
||||
|
||||
self.in_degree_map = self.build_in_degree()
|
||||
self.parent_child_map = self.build_parent_child_map()
|
||||
if vertices is None:
|
||||
vertices = self.vertices
|
||||
|
||||
self.predecessor_map, self.successor_map = self.build_adjacency_maps(edges)
|
||||
|
||||
self.in_degree_map = self.build_in_degree(edges)
|
||||
self.parent_child_map = self.build_parent_child_map(vertices)
|
||||
|
||||
def reset_inactivated_vertices(self):
|
||||
"""
|
||||
|
|
@ -427,15 +469,22 @@ class Graph:
|
|||
vertex = self.get_vertex(vertex_id)
|
||||
vertex.set_state(state)
|
||||
|
||||
def mark_branch(self, vertex_id: str, state: str):
|
||||
def mark_branch(self, vertex_id: str, state: str, visited: Optional[set] = None):
|
||||
"""Marks a branch of the graph."""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
if vertex_id in visited:
|
||||
return
|
||||
visited.add(vertex_id)
|
||||
|
||||
self.mark_vertex(vertex_id, state)
|
||||
|
||||
for child_id in self.parent_child_map[vertex_id]:
|
||||
self.mark_branch(child_id, state)
|
||||
|
||||
def build_parent_child_map(self):
|
||||
def build_parent_child_map(self, vertices: List[Vertex]):
|
||||
parent_child_map = defaultdict(list)
|
||||
for vertex in self.vertices:
|
||||
for vertex in vertices:
|
||||
parent_child_map[vertex.id] = [child.id for child in self.get_successors(vertex)]
|
||||
return parent_child_map
|
||||
|
||||
|
|
@ -559,6 +608,7 @@ class Graph:
|
|||
self.update_vertex_from_another(self_vertex, other_vertex)
|
||||
|
||||
self.build_graph_maps()
|
||||
self.define_vertices_lists()
|
||||
self.increment_update_count()
|
||||
return self
|
||||
|
||||
|
|
@ -668,6 +718,7 @@ class Graph:
|
|||
inputs_dict: Optional[Dict[str, str]] = None,
|
||||
files: Optional[list[str]] = None,
|
||||
user_id: Optional[str] = None,
|
||||
fallback_to_env_vars: bool = False,
|
||||
):
|
||||
"""
|
||||
Builds a vertex in the graph.
|
||||
|
|
@ -689,7 +740,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, files=files)
|
||||
await vertex.build(user_id=user_id, inputs=inputs_dict,files=files, fallback_to_env_vars=fallback_to_env_vars)
|
||||
|
||||
if vertex.result is not None:
|
||||
params = vertex._built_object_repr()
|
||||
|
|
@ -752,7 +803,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)
|
||||
|
|
@ -777,6 +828,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)}",
|
||||
)
|
||||
|
|
@ -857,7 +909,7 @@ class Graph:
|
|||
"""Returns the predecessors of a vertex."""
|
||||
return [self.get_vertex(source_id) for source_id in self.predecessor_map.get(vertex.id, [])]
|
||||
|
||||
def get_all_successors(self, vertex, recursive=True, flat=True):
|
||||
def get_all_successors(self, vertex: Vertex, recursive=True, flat=True):
|
||||
# Recursively get the successors of the current vertex
|
||||
# successors = vertex.successors
|
||||
# if not successors:
|
||||
|
|
@ -894,7 +946,7 @@ class Graph:
|
|||
successors_result.append([successor])
|
||||
return successors_result
|
||||
|
||||
def get_successors(self, vertex):
|
||||
def get_successors(self, vertex: Vertex) -> List[Vertex]:
|
||||
"""Returns the successors of a vertex."""
|
||||
return [self.get_vertex(target_id) for target_id in self.successor_map.get(vertex.id, [])]
|
||||
|
||||
|
|
@ -943,10 +995,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
|
||||
elif node_name in ["ShouldRunNext"]:
|
||||
return RoutingVertex
|
||||
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:
|
||||
|
|
@ -1278,17 +1328,17 @@ class Graph:
|
|||
def remove_from_predecessors(self, vertex_id: str):
|
||||
self.run_manager.remove_from_predecessors(vertex_id)
|
||||
|
||||
def build_in_degree(self):
|
||||
in_degree = defaultdict(int)
|
||||
for edge in self.edges:
|
||||
def build_in_degree(self, edges: List[ContractEdge]) -> Dict[str, int]:
|
||||
in_degree: Dict[str, int] = defaultdict(int)
|
||||
for edge in edges:
|
||||
in_degree[edge.target_id] += 1
|
||||
return in_degree
|
||||
|
||||
def build_adjacency_maps(self):
|
||||
def build_adjacency_maps(self, edges: List[ContractEdge]) -> Tuple[Dict[str, List[str]], Dict[str, List[str]]]:
|
||||
"""Returns the adjacency maps for the graph."""
|
||||
predecessor_map = defaultdict(list)
|
||||
successor_map = defaultdict(list)
|
||||
for edge in self.edges:
|
||||
for edge in edges:
|
||||
predecessor_map[edge.target_id].append(edge.source_id)
|
||||
successor_map[edge.source_id].append(edge.target_id)
|
||||
return predecessor_map, successor_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
|
||||
|
|
@ -5,7 +6,6 @@ from langflow.interface.document_loaders.base import documentloader_creator
|
|||
from langflow.interface.embeddings.base import embedding_creator
|
||||
from langflow.interface.llms.base import llm_creator
|
||||
from langflow.interface.memories.base import memory_creator
|
||||
from langflow.interface.output_parsers.base import output_parser_creator
|
||||
from langflow.interface.prompts.base import prompt_creator
|
||||
from langflow.interface.retrievers.base import retriever_creator
|
||||
from langflow.interface.text_splitters.base import textsplitter_creator
|
||||
|
|
@ -14,9 +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"]
|
||||
ROUTING_COMPONENTS = ["ShouldRunNext"]
|
||||
|
||||
|
||||
class VertexTypesDict(LazyLoadDictBase):
|
||||
def __init__(self):
|
||||
|
|
@ -47,11 +44,9 @@ class VertexTypesDict(LazyLoadDictBase):
|
|||
# **{t: types.VectorStoreVertex for t in vectorstore_creator.to_list()},
|
||||
**{t: types.DocumentLoaderVertex for t in documentloader_creator.to_list()},
|
||||
**{t: types.TextSplitterVertex for t in textsplitter_creator.to_list()},
|
||||
**{t: types.OutputParserVertex for t in output_parser_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.RoutingVertex for t in ROUTING_COMPONENTS},
|
||||
**{t: types.InterfaceVertex for t in CHAT_COMPONENTS},
|
||||
}
|
||||
|
||||
def get_custom_component_vertex_type(self):
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class RunnableVerticesManager:
|
|||
|
||||
def is_vertex_runnable(self, vertex_id: str) -> bool:
|
||||
"""Determines if a vertex is runnable."""
|
||||
|
||||
return vertex_id in self.vertices_to_run and not self.run_predecessors.get(vertex_id)
|
||||
|
||||
def find_runnable_predecessors_for_successors(self, vertex_id: str) -> List[str]:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -72,7 +72,6 @@ class Vertex:
|
|||
self.load_from_db_fields: List[str] = []
|
||||
self.parent_is_top_level = False
|
||||
self.layer = None
|
||||
self.should_run = True
|
||||
self.result: Optional[ResultData] = None
|
||||
try:
|
||||
self.is_interface_component = self.vertex_type in InterfaceComponentTypes
|
||||
|
|
@ -316,7 +315,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":
|
||||
|
|
@ -391,13 +394,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 +441,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 +513,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 +528,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 +538,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 +550,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 +578,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 +621,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 +630,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
|
||||
|
||||
|
||||
|
|
@ -300,11 +300,6 @@ class PromptVertex(Vertex):
|
|||
return str(self._built_object)
|
||||
|
||||
|
||||
class OutputParserVertex(Vertex):
|
||||
def __init__(self, data: Dict, graph):
|
||||
super().__init__(data, graph=graph, base_type="output_parsers")
|
||||
|
||||
|
||||
class CustomComponentVertex(Vertex):
|
||||
def __init__(self, data: Dict, graph):
|
||||
super().__init__(data, graph=graph, base_type="custom_components")
|
||||
|
|
@ -314,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]
|
||||
|
|
@ -330,52 +325,133 @@ 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)
|
||||
files = [{"path": file} if isinstance(file, str) else file for file in self.params.get("files", [])]
|
||||
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,
|
||||
files=files
|
||||
)
|
||||
|
||||
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)
|
||||
files = [{"path": file} if isinstance(file, str) else file for file in self.params.get("files", [])]
|
||||
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, files=files
|
||||
)
|
||||
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, files=files
|
||||
)
|
||||
|
||||
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
|
||||
|
|
@ -436,41 +512,6 @@ class ChatVertex(Vertex):
|
|||
return self.vertex_type == InterfaceComponentTypes.ChatInput and self.is_input
|
||||
|
||||
|
||||
class RoutingVertex(Vertex):
|
||||
def __init__(self, data: Dict, graph):
|
||||
super().__init__(data, graph=graph, base_type="custom_components")
|
||||
self.use_result = True
|
||||
self.steps = [self._build]
|
||||
|
||||
def _built_object_repr(self):
|
||||
if self.artifacts and "repr" in self.artifacts:
|
||||
return self.artifacts["repr"] or super()._built_object_repr()
|
||||
return super()._built_object_repr()
|
||||
|
||||
@property
|
||||
def successors_ids(self):
|
||||
if isinstance(self._built_object, bool):
|
||||
ids = super().successors_ids
|
||||
if self._built_object:
|
||||
return ids
|
||||
return []
|
||||
raise ValueError("RoutingVertex should return a boolean value.")
|
||||
|
||||
def _run(self, *args, **kwargs):
|
||||
if self._built_object:
|
||||
condition = self._built_object.get("condition")
|
||||
result = self._built_object.get("result")
|
||||
if condition is None:
|
||||
raise ValueError("Condition is required for the routing vertex.")
|
||||
if result is None:
|
||||
raise ValueError("Result is required for the routing vertex.")
|
||||
if condition is True:
|
||||
self._built_result = result
|
||||
else:
|
||||
self.graph.mark_branch(self.id, "INACTIVE")
|
||||
self._built_result = None
|
||||
|
||||
|
||||
class StateVertex(Vertex):
|
||||
def __init__(self, data: Dict, graph):
|
||||
super().__init__(data, graph=graph, base_type="custom_components")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -45,9 +45,7 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -86,22 +84,14 @@
|
|||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["object", "str", "Text"],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"user_input"
|
||||
]
|
||||
"template": ["user_input"]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
|
|
@ -150,9 +140,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -161,7 +149,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-2024-04-09\",\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -212,7 +200,7 @@
|
|||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
|
|
@ -222,14 +210,11 @@
|
|||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4o",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
"gpt-3.5-turbo-0125"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
|
|
@ -238,9 +223,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -259,9 +242,7 @@
|
|||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -280,9 +261,7 @@
|
|||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": true,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
|
|
@ -321,9 +300,7 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -354,11 +331,7 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["object", "Text", "str"],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -372,9 +345,7 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -421,7 +392,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n files: Optional[list[str]] = None,\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n files=files,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -445,9 +416,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -471,9 +440,7 @@
|
|||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
|
|
@ -505,10 +472,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -516,9 +480,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -538,9 +500,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -559,20 +519,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"Record",
|
||||
"Text",
|
||||
"str",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["Record", "Text", "str", "object"],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -583,10 +536,7 @@
|
|||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -621,7 +571,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Interaction Panel.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -682,10 +632,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -693,9 +640,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -715,9 +660,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -736,20 +679,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get chat inputs from the Interaction Panel.",
|
||||
"description": "Get chat inputs from the Playground.",
|
||||
"icon": "ChatInput",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["object", "Record", "str", "Text"],
|
||||
"display_name": "Chat Input",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -759,10 +695,7 @@
|
|||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -790,17 +723,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-njtka",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"baseClasses": ["object", "Text", "str"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-k39HS"
|
||||
}
|
||||
|
|
@ -820,17 +747,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-k39HS",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-uxBqP"
|
||||
}
|
||||
|
|
@ -850,21 +771,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "user_input",
|
||||
"id": "Prompt-uxBqP",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "Record", "str", "Text"],
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-P3fgL"
|
||||
}
|
||||
|
|
@ -886,4 +797,4 @@
|
|||
"name": "Basic Prompting (Hello, World)",
|
||||
"last_tested_version": "1.0.0a4",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,7 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -138,24 +136,14 @@
|
|||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["object", "Text", "str"],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"reference_1",
|
||||
"reference_2",
|
||||
"instructions"
|
||||
]
|
||||
"template": ["reference_1", "reference_2", "instructions"]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
|
|
@ -222,9 +210,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": [
|
||||
"https://www.promptingguide.ai/techniques/prompt_chaining"
|
||||
]
|
||||
|
|
@ -233,17 +219,13 @@
|
|||
},
|
||||
"description": "Fetch content from one or more URLs.",
|
||||
"icon": "layout-template",
|
||||
"base_classes": [
|
||||
"Record"
|
||||
],
|
||||
"base_classes": ["Record"],
|
||||
"display_name": "URL",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"urls": null
|
||||
},
|
||||
"output_types": [
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -278,7 +260,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n files: Optional[list[str]] = None,\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n files=files,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -302,9 +284,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -328,9 +308,7 @@
|
|||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
|
|
@ -362,10 +340,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -373,9 +348,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -395,9 +368,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -416,20 +387,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"Text",
|
||||
"Record",
|
||||
"object",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["Text", "Record", "object", "str"],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -440,10 +404,7 @@
|
|||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -483,9 +444,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -494,7 +453,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-2024-04-09\",\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -545,7 +504,7 @@
|
|||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
|
|
@ -555,14 +514,11 @@
|
|||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4o",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
"gpt-3.5-turbo-0125"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
|
|
@ -571,9 +527,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -592,9 +546,7 @@
|
|||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -613,9 +565,7 @@
|
|||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
|
|
@ -654,9 +604,7 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -687,11 +635,7 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -705,9 +649,7 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -780,28 +722,20 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"value": [
|
||||
"https://www.promptingguide.ai/introduction/basics"
|
||||
]
|
||||
"input_types": ["Text"],
|
||||
"value": ["https://www.promptingguide.ai/introduction/basics"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Fetch content from one or more URLs.",
|
||||
"icon": "layout-template",
|
||||
"base_classes": [
|
||||
"Record"
|
||||
],
|
||||
"base_classes": ["Record"],
|
||||
"display_name": "URL",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"urls": null
|
||||
},
|
||||
"output_types": [
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -836,7 +770,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextInput(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Interaction Panel.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as input.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Optional[str] = \"\",\n record_template: Optional[str] = \"\",\n ) -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextInput(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as input.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Optional[str] = \"\",\n record_template: Optional[str] = \"\",\n ) -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -861,10 +795,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Value",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Record", "Text"],
|
||||
"dynamic": false,
|
||||
"info": "Text or Record to be passed as input.",
|
||||
"load_from_db": false,
|
||||
|
|
@ -888,28 +819,20 @@
|
|||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get text inputs from the Interaction Panel.",
|
||||
"description": "Get text inputs from the Playground.",
|
||||
"icon": "type",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["object", "Text", "str"],
|
||||
"display_name": "Instructions",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"input_value": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -938,18 +861,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "reference_2",
|
||||
"id": "Prompt-Rse03",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Record"
|
||||
],
|
||||
"baseClasses": ["Record"],
|
||||
"dataType": "URL",
|
||||
"id": "URL-HYPkR"
|
||||
}
|
||||
|
|
@ -969,17 +885,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-JPlxl",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-gi29P"
|
||||
}
|
||||
|
|
@ -999,18 +909,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "reference_1",
|
||||
"id": "Prompt-Rse03",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Record"
|
||||
],
|
||||
"baseClasses": ["Record"],
|
||||
"dataType": "URL",
|
||||
"id": "URL-2cX90"
|
||||
}
|
||||
|
|
@ -1030,20 +933,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "instructions",
|
||||
"id": "Prompt-Rse03",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"baseClasses": ["object", "Text", "str"],
|
||||
"dataType": "TextInput",
|
||||
"id": "TextInput-og8Or"
|
||||
}
|
||||
|
|
@ -1063,17 +957,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-gi29P",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"baseClasses": ["object", "Text", "str"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-Rse03"
|
||||
}
|
||||
|
|
@ -1096,4 +984,4 @@
|
|||
"name": "Blog Writer",
|
||||
"last_tested_version": "1.0.0a0",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,7 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -112,23 +110,14 @@
|
|||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["object", "str", "Text"],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"Document",
|
||||
"Question"
|
||||
]
|
||||
"template": ["Document", "Question"]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
|
|
@ -231,18 +220,14 @@
|
|||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "A generic file loader.",
|
||||
"base_classes": [
|
||||
"Record"
|
||||
],
|
||||
"base_classes": ["Record"],
|
||||
"display_name": "Files",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"path": null,
|
||||
"silent_errors": null
|
||||
},
|
||||
"output_types": [
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -277,7 +262,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Interaction Panel.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -338,10 +323,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -349,9 +331,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -371,9 +351,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -392,20 +370,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get chat inputs from the Interaction Panel.",
|
||||
"description": "Get chat inputs from the Playground.",
|
||||
"icon": "ChatInput",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Record",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Record", "Text", "object"],
|
||||
"display_name": "Chat Input",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -415,10 +386,7 @@
|
|||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -453,7 +421,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n files: Optional[list[str]] = None,\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n files=files,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -477,9 +445,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -515,10 +481,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -526,9 +489,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -548,9 +509,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -569,20 +528,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Record",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Record", "Text", "object"],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -592,10 +544,7 @@
|
|||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -640,9 +589,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -651,7 +598,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-2024-04-09\",\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -702,7 +649,7 @@
|
|||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
|
|
@ -712,14 +659,11 @@
|
|||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4o",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
"gpt-3.5-turbo-0125"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
|
|
@ -728,9 +672,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -749,9 +691,7 @@
|
|||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -770,9 +710,7 @@
|
|||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
|
|
@ -811,9 +749,7 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -844,11 +780,7 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["object", "str", "Text"],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -862,9 +794,7 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -902,21 +832,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "Question",
|
||||
"id": "Prompt-tHwPf",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Record",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Record", "Text", "object"],
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-MsSJ9"
|
||||
}
|
||||
|
|
@ -936,18 +856,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "Document",
|
||||
"id": "Prompt-tHwPf",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Record"
|
||||
],
|
||||
"baseClasses": ["Record"],
|
||||
"dataType": "File",
|
||||
"id": "File-6TEsD"
|
||||
}
|
||||
|
|
@ -967,17 +880,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-Bt067",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-tHwPf"
|
||||
}
|
||||
|
|
@ -997,17 +904,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-F5Awj",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-Bt067"
|
||||
}
|
||||
|
|
@ -1029,4 +930,4 @@
|
|||
"name": "Document QA",
|
||||
"last_tested_version": "1.0.0a0",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Interaction Panel.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n files: Optional[list[str]] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n files=files,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -83,10 +83,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -94,9 +91,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -116,9 +111,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -137,21 +130,14 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": "MySessionID"
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get chat inputs from the Interaction Panel.",
|
||||
"description": "Get chat inputs from the Playground.",
|
||||
"icon": "ChatInput",
|
||||
"base_classes": [
|
||||
"Text",
|
||||
"object",
|
||||
"Record",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["Text", "object", "Record", "str"],
|
||||
"display_name": "Chat Input",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -161,10 +147,7 @@
|
|||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -199,7 +182,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n files: Optional[list[str]] = None,\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n files=files,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -223,9 +206,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -261,10 +242,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -272,9 +250,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -294,9 +270,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -315,21 +289,14 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": "MySessionID"
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"Text",
|
||||
"object",
|
||||
"Record",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["Text", "object", "Record", "str"],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -339,10 +306,7 @@
|
|||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -377,7 +341,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import records_to_text\nfrom langflow.interface.custom.custom_component import CustomComponent\nfrom langflow.memory import get_messages\n\n\nclass MemoryComponent(CustomComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n order = \"DESC\" if order == \"Descending\" else \"ASC\"\n if sender == \"Machine and User\":\n sender = None\n messages = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = records_to_text(template=record_template or \"\", records=messages)\n self.status = messages_str\n return messages_str\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.memory.memory import BaseMemoryComponent\nfrom langflow.field_typing import Text\nfrom langflow.helpers.record import records_to_text\nfrom langflow.memory import get_messages\nfrom langflow.schema.schema import Record\n\n\nclass MemoryComponent(BaseMemoryComponent):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages given a specific Session ID.\"\n beta: bool = True\n icon = \"history\"\n\n def build_config(self):\n return {\n \"sender\": {\n \"options\": [\"Machine\", \"User\", \"Machine and User\"],\n \"display_name\": \"Sender Type\",\n },\n \"sender_name\": {\"display_name\": \"Sender Name\", \"advanced\": True},\n \"n_messages\": {\n \"display_name\": \"Number of Messages\",\n \"info\": \"Number of messages to retrieve.\",\n },\n \"session_id\": {\n \"display_name\": \"Session ID\",\n \"info\": \"Session ID of the chat history.\",\n \"input_types\": [\"Text\"],\n },\n \"order\": {\n \"options\": [\"Ascending\", \"Descending\"],\n \"display_name\": \"Order\",\n \"info\": \"Order of the messages.\",\n \"advanced\": True,\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def get_messages(self, **kwargs) -> list[Record]:\n # Validate kwargs by checking if it contains the correct keys\n if \"sender\" not in kwargs:\n kwargs[\"sender\"] = None\n if \"sender_name\" not in kwargs:\n kwargs[\"sender_name\"] = None\n if \"session_id\" not in kwargs:\n kwargs[\"session_id\"] = None\n if \"limit\" not in kwargs:\n kwargs[\"limit\"] = 5\n if \"order\" not in kwargs:\n kwargs[\"order\"] = \"Descending\"\n\n kwargs[\"order\"] = \"DESC\" if kwargs[\"order\"] == \"Descending\" else \"ASC\"\n if kwargs[\"sender\"] == \"Machine and User\":\n kwargs[\"sender\"] = None\n return get_messages(**kwargs)\n\n def build(\n self,\n sender: Optional[str] = \"Machine and User\",\n sender_name: Optional[str] = None,\n session_id: Optional[str] = None,\n n_messages: int = 5,\n order: Optional[str] = \"Descending\",\n record_template: Optional[str] = \"{sender_name}: {text}\",\n ) -> Text:\n messages = self.get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n messages_str = records_to_text(template=record_template or \"\", records=messages)\n self.status = messages_str\n return messages_str\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -418,10 +382,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Ascending",
|
||||
"Descending"
|
||||
],
|
||||
"options": ["Ascending", "Descending"],
|
||||
"name": "order",
|
||||
"display_name": "Order",
|
||||
"advanced": true,
|
||||
|
|
@ -429,9 +390,7 @@
|
|||
"info": "Order of the messages.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"record_template": {
|
||||
"type": "str",
|
||||
|
|
@ -451,9 +410,7 @@
|
|||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender": {
|
||||
"type": "str",
|
||||
|
|
@ -466,11 +423,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User",
|
||||
"Machine and User"
|
||||
],
|
||||
"options": ["Machine", "User", "Machine and User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": false,
|
||||
|
|
@ -478,9 +431,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -499,9 +450,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -516,9 +465,7 @@
|
|||
"name": "session_id",
|
||||
"display_name": "Session ID",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "Session ID of the chat history.",
|
||||
"load_from_db": false,
|
||||
|
|
@ -529,11 +476,7 @@
|
|||
},
|
||||
"description": "Retrieves stored chat messages given a specific Session ID.",
|
||||
"icon": "history",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "Chat Memory",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -544,9 +487,7 @@
|
|||
"order": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -608,9 +549,7 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -675,23 +614,14 @@
|
|||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"Text",
|
||||
"str",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["Text", "str", "object"],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"context",
|
||||
"user_message"
|
||||
]
|
||||
"template": ["context", "user_message"]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
|
|
@ -740,9 +670,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -751,7 +679,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-2024-04-09\",\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -802,7 +730,7 @@
|
|||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
|
|
@ -812,14 +740,11 @@
|
|||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4o",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
"gpt-3.5-turbo-0125"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
|
|
@ -828,9 +753,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -849,9 +772,7 @@
|
|||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -870,9 +791,7 @@
|
|||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
|
|
@ -911,9 +830,7 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -944,11 +861,7 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"object",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["str", "object", "Text"],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -962,9 +875,7 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -1016,10 +927,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Value",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Record", "Text"],
|
||||
"dynamic": false,
|
||||
"info": "Text or Record to be passed as output.",
|
||||
"load_from_db": false,
|
||||
|
|
@ -1032,7 +940,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Interaction Panel.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -1061,28 +969,20 @@
|
|||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a text output in the Interaction Panel.",
|
||||
"description": "Display a text output in the Playground.",
|
||||
"icon": "type",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"object",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["str", "object", "Text"],
|
||||
"display_name": "Inspect Memory",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"input_value": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -1111,19 +1011,10 @@
|
|||
"fieldName": "context",
|
||||
"type": "str",
|
||||
"id": "Prompt-ODkUx",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
]
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"]
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "MemoryComponent",
|
||||
"id": "MemoryComponent-cdA1J"
|
||||
}
|
||||
|
|
@ -1145,20 +1036,10 @@
|
|||
"fieldName": "user_message",
|
||||
"type": "str",
|
||||
"id": "Prompt-ODkUx",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
]
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"]
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Text",
|
||||
"object",
|
||||
"Record",
|
||||
"str"
|
||||
],
|
||||
"baseClasses": ["Text", "object", "Record", "str"],
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-t7F8v"
|
||||
}
|
||||
|
|
@ -1179,17 +1060,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-9RykF",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Text",
|
||||
"str",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["Text", "str", "object"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-ODkUx"
|
||||
}
|
||||
|
|
@ -1209,17 +1084,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-P1jEe",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"object",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["str", "object", "Text"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-9RykF"
|
||||
}
|
||||
|
|
@ -1239,18 +1108,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "TextOutput-vrs6T",
|
||||
"inputTypes": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "MemoryComponent",
|
||||
"id": "MemoryComponent-cdA1J"
|
||||
}
|
||||
|
|
@ -1272,4 +1134,4 @@
|
|||
"name": "Memory Chatbot",
|
||||
"last_tested_version": "1.0.0a0",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,7 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -86,22 +84,14 @@
|
|||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["object", "str", "Text"],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"document"
|
||||
]
|
||||
"template": ["document"]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
|
|
@ -165,9 +155,7 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -206,22 +194,14 @@
|
|||
"is_input": null,
|
||||
"is_output": null,
|
||||
"is_composition": null,
|
||||
"base_classes": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"base_classes": ["object", "str", "Text"],
|
||||
"name": "",
|
||||
"display_name": "Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"template": [
|
||||
"summary"
|
||||
]
|
||||
"template": ["summary"]
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"full_path": null,
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
|
|
@ -256,7 +236,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n files: Optional[list[str]] = None,\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n files=files,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -280,9 +260,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -306,9 +284,7 @@
|
|||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
|
|
@ -340,10 +316,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -351,9 +324,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -373,9 +344,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -394,20 +363,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Record",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["object", "Record", "Text", "str"],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -418,10 +380,7 @@
|
|||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -452,7 +411,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n files: Optional[list[str]] = None,\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n files=files,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -476,9 +435,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -502,9 +459,7 @@
|
|||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
|
|
@ -536,10 +491,7 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"options": ["Machine", "User"],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -547,9 +499,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -569,9 +519,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -590,20 +538,13 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Record",
|
||||
"Text",
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["object", "Record", "Text", "str"],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -614,10 +555,7 @@
|
|||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"output_types": ["Text", "Record"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -647,7 +585,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextInput(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Interaction Panel.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as input.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Optional[Text] = \"\",\n record_template: Optional[str] = \"\",\n ) -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextInput(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as input.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Optional[Text] = \"\",\n record_template: Optional[str] = \"\",\n ) -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -672,10 +610,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Value",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Record", "Text"],
|
||||
"dynamic": false,
|
||||
"info": "Text or Record to be passed as input.",
|
||||
"load_from_db": false,
|
||||
|
|
@ -699,28 +634,20 @@
|
|||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get text inputs from the Interaction Panel.",
|
||||
"description": "Get text inputs from the Playground.",
|
||||
"icon": "type",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "Text Input",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"input_value": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -762,10 +689,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Value",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Record", "Text"],
|
||||
"dynamic": false,
|
||||
"info": "Text or Record to be passed as output.",
|
||||
"load_from_db": false,
|
||||
|
|
@ -778,7 +702,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Interaction Panel.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -807,28 +731,20 @@
|
|||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a text output in the Interaction Panel.",
|
||||
"description": "Display a text output in the Playground.",
|
||||
"icon": "type",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "First Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"input_value": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -873,9 +789,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -884,7 +798,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-2024-04-09\",\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -935,7 +849,7 @@
|
|||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
|
|
@ -945,14 +859,11 @@
|
|||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4o",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
"gpt-3.5-turbo-0125"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
|
|
@ -961,9 +872,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -982,9 +891,7 @@
|
|||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -1003,9 +910,7 @@
|
|||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
|
|
@ -1044,9 +949,7 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -1077,11 +980,7 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -1095,9 +994,7 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -1149,10 +1046,7 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Value",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Record", "Text"],
|
||||
"dynamic": false,
|
||||
"info": "Text or Record to be passed as output.",
|
||||
"load_from_db": false,
|
||||
|
|
@ -1165,7 +1059,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Interaction Panel.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"value": "from typing import Optional\n\nfrom langflow.base.io.text import TextComponent\nfrom langflow.field_typing import Text\n\n\nclass TextOutput(TextComponent):\n display_name = \"Text Output\"\n description = \"Display a text output in the Playground.\"\n icon = \"type\"\n\n def build_config(self):\n return {\n \"input_value\": {\n \"display_name\": \"Value\",\n \"input_types\": [\"Record\", \"Text\"],\n \"info\": \"Text or Record to be passed as output.\",\n },\n \"record_template\": {\n \"display_name\": \"Record Template\",\n \"multiline\": True,\n \"info\": \"Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.\",\n \"advanced\": True,\n },\n }\n\n def build(self, input_value: Optional[Text] = \"\", record_template: str = \"\") -> Text:\n return super().build(input_value=input_value, record_template=record_template)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -1194,28 +1088,20 @@
|
|||
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a text output in the Interaction Panel.",
|
||||
"description": "Display a text output in the Playground.",
|
||||
"icon": "type",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "Second Prompt",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"input_value": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -1260,9 +1146,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -1271,7 +1155,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": [\n \"gpt-4-turbo-2024-04-09\",\n \"gpt-4-turbo-preview\",\n \"gpt-3.5-turbo\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n \"value\": \"gpt-4-turbo-preview\",\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -1322,7 +1206,7 @@
|
|||
},
|
||||
"model_name": {
|
||||
"type": "str",
|
||||
"required": true,
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"list": true,
|
||||
"show": true,
|
||||
|
|
@ -1332,14 +1216,11 @@
|
|||
"file_path": "",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4o",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106"
|
||||
"gpt-3.5-turbo-0125"
|
||||
],
|
||||
"name": "model_name",
|
||||
"display_name": "Model Name",
|
||||
|
|
@ -1348,9 +1229,7 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -1369,9 +1248,7 @@
|
|||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -1390,9 +1267,7 @@
|
|||
"info": "The OpenAI API Key to use for the OpenAI model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"input_types": ["Text"],
|
||||
"value": ""
|
||||
},
|
||||
"stream": {
|
||||
|
|
@ -1431,9 +1306,7 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -1464,11 +1337,7 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -1482,9 +1351,7 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"output_types": ["Text"],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -1522,20 +1389,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "document",
|
||||
"id": "Prompt-amqBu",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "TextInput",
|
||||
"id": "TextInput-sptaH"
|
||||
}
|
||||
|
|
@ -1555,18 +1413,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "TextOutput-2MS4a",
|
||||
"inputTypes": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-amqBu"
|
||||
}
|
||||
|
|
@ -1586,17 +1437,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-uYXZJ",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-amqBu"
|
||||
}
|
||||
|
|
@ -1616,20 +1461,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "summary",
|
||||
"id": "Prompt-gTNiz",
|
||||
"inputTypes": [
|
||||
"Document",
|
||||
"BaseOutputParser",
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-uYXZJ"
|
||||
}
|
||||
|
|
@ -1649,17 +1485,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-EJkG3",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-uYXZJ"
|
||||
}
|
||||
|
|
@ -1679,18 +1509,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "TextOutput-MUDOR",
|
||||
"inputTypes": [
|
||||
"Record",
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Record", "Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-gTNiz"
|
||||
}
|
||||
|
|
@ -1710,17 +1533,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "OpenAIModel-XawYB",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"baseClasses": ["object", "str", "Text"],
|
||||
"dataType": "Prompt",
|
||||
"id": "Prompt-gTNiz"
|
||||
}
|
||||
|
|
@ -1740,17 +1557,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-DNmvg",
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"inputTypes": ["Text"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"baseClasses": ["str", "Text", "object"],
|
||||
"dataType": "OpenAIModel",
|
||||
"id": "OpenAIModel-XawYB"
|
||||
}
|
||||
|
|
@ -1772,4 +1583,4 @@
|
|||
"name": "Prompt Chaining",
|
||||
"last_tested_version": "1.0.0a0",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@ from langchain.agents.agent_toolkits.vectorstore.prompt import ROUTER_PREFIX as
|
|||
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.chains.llm import LLMChain
|
||||
from langchain.sql_database import SQLDatabase
|
||||
from langchain_community.utilities import SQLDatabase
|
||||
from langchain.tools.sql_database.prompt import QUERY_CHECKER
|
||||
from langchain_community.agent_toolkits import SQLDatabaseToolkit
|
||||
from langchain_community.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
|
||||
|
|
|
|||
|
|
@ -87,6 +87,14 @@ class CustomComponent(Component):
|
|||
except Exception as e:
|
||||
raise ValueError(f"Error updating state: {e}")
|
||||
|
||||
def stop(self):
|
||||
if not self.vertex:
|
||||
raise ValueError("Vertex is not set")
|
||||
try:
|
||||
self.graph.mark_branch(self.vertex.id, "INACTIVE")
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error stopping {self.display_name}: {e}")
|
||||
|
||||
def append_state(self, name: str, value: Any):
|
||||
if not self.vertex:
|
||||
raise ValueError("Vertex is not set")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import inspect
|
||||
from typing import Any
|
||||
|
||||
from langchain import llms, memory, requests, text_splitter
|
||||
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
|
||||
|
||||
|
|
@ -43,8 +43,6 @@ memory_type_to_cls_dict: dict[str, Any] = {
|
|||
memory_name: import_class(f"langchain.memory.{memory_name}") for memory_name in memory.__all__
|
||||
}
|
||||
|
||||
# Wrappers
|
||||
wrapper_type_to_cls_dict: dict[str, Any] = {wrapper.__name__: wrapper for wrapper in [requests.RequestsWrapper]}
|
||||
|
||||
# Embeddings
|
||||
embedding_type_to_cls_dict: dict[str, Any] = {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ def import_by_type(_type: str, name: str) -> Any:
|
|||
"documentloaders": import_documentloader,
|
||||
"textsplitters": import_textsplitter,
|
||||
"utilities": import_utility,
|
||||
"output_parsers": import_output_parser,
|
||||
"retrievers": import_retriever,
|
||||
}
|
||||
if _type == "models":
|
||||
|
|
@ -57,11 +56,6 @@ def import_by_type(_type: str, name: str) -> Any:
|
|||
return loaded_func(name)
|
||||
|
||||
|
||||
def import_output_parser(output_parser: str) -> Any:
|
||||
"""Import output parser from output parser name"""
|
||||
return import_module(f"from langchain.output_parsers import {output_parser}")
|
||||
|
||||
|
||||
def import_chat_llm(llm: str) -> BaseChatModel:
|
||||
"""Import chat llm from llm name"""
|
||||
return import_class(f"langchain_community.chat_models.{llm}")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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
|
||||
|
|
@ -20,7 +20,6 @@ from langflow.interface.importing.utils import import_by_type
|
|||
from langflow.interface.initialize.llm import initialize_vertexai
|
||||
from langflow.interface.initialize.utils import handle_format_kwargs, handle_node_type, handle_partial_variables
|
||||
from langflow.interface.initialize.vector_store import vecstore_initializer
|
||||
from langflow.interface.output_parsers.base import output_parser_creator
|
||||
from langflow.interface.retrievers.base import retriever_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.utils import load_file_into_dict
|
||||
|
|
@ -36,6 +35,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"""
|
||||
|
|
@ -58,7 +58,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,
|
||||
|
|
@ -67,6 +67,7 @@ async def instantiate_class(
|
|||
params=params,
|
||||
user_id=user_id,
|
||||
vertex=vertex,
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -94,14 +95,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":
|
||||
|
|
@ -126,8 +120,6 @@ async def instantiate_based_on_type(
|
|||
return instantiate_utility(node_type, class_object, params)
|
||||
elif base_type == "chains":
|
||||
return instantiate_chains(node_type, class_object, params)
|
||||
elif base_type == "output_parsers":
|
||||
return instantiate_output_parser(node_type, class_object, params)
|
||||
elif base_type == "models":
|
||||
return instantiate_llm(node_type, class_object, params)
|
||||
elif base_type == "retrievers":
|
||||
|
|
@ -135,33 +127,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(
|
||||
|
|
@ -170,7 +178,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()
|
||||
|
|
@ -185,7 +195,7 @@ async def instantiate_custom_component(params, user_id, vertex):
|
|||
# Call the build method directly if it's sync
|
||||
build_result = custom_component.build(**params_copy)
|
||||
custom_repr = custom_component.custom_repr()
|
||||
if not custom_repr and isinstance(build_result, (dict, Record, str)):
|
||||
if custom_repr is None and isinstance(build_result, (dict, Record, str)):
|
||||
custom_repr = build_result
|
||||
if not isinstance(custom_repr, str):
|
||||
custom_repr = str(custom_repr)
|
||||
|
|
@ -201,15 +211,6 @@ def instantiate_wrapper(node_type, class_object, params):
|
|||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_output_parser(node_type, class_object, params):
|
||||
if node_type in output_parser_creator.from_method_nodes:
|
||||
method = output_parser_creator.from_method_nodes[node_type]
|
||||
if class_method := getattr(class_object, method, None):
|
||||
return class_method(**params)
|
||||
raise ValueError(f"Method {method} not found in {class_object}")
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_llm(node_type, class_object, params: Dict):
|
||||
# This is a workaround so JinaChat works until streaming is implemented
|
||||
# if "openai_api_base" in params and "jina" in params["openai_api_base"]:
|
||||
|
|
@ -514,15 +515,6 @@ def build_prompt_template(prompt, tools):
|
|||
"show": False,
|
||||
"multiline": False,
|
||||
},
|
||||
"output_parser": {
|
||||
"type": "BaseOutputParser",
|
||||
"required": False,
|
||||
"placeholder": "",
|
||||
"list": False,
|
||||
"show": False,
|
||||
"multline": False,
|
||||
"value": None,
|
||||
},
|
||||
"template": {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import orjson
|
|||
from langchain_community.vectorstores import (
|
||||
FAISS,
|
||||
Chroma,
|
||||
ElasticsearchStore,
|
||||
MongoDBAtlasVectorSearch,
|
||||
Pinecone,
|
||||
Qdrant,
|
||||
|
|
@ -227,34 +226,11 @@ def initialize_qdrant(class_object: Type[Qdrant], params: dict):
|
|||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
def initialize_elasticsearch(class_object: Type[ElasticsearchStore], params: dict):
|
||||
"""Initialize elastic and return the class object"""
|
||||
if "index_name" not in params:
|
||||
raise ValueError("Elasticsearch Index must be provided in the params")
|
||||
if "es_url" not in params:
|
||||
raise ValueError("Elasticsearch URL must be provided in the params")
|
||||
if not docs_in_params(params):
|
||||
existing_index_params = {
|
||||
"embedding": params.pop("embedding"),
|
||||
}
|
||||
if "index_name" in params:
|
||||
existing_index_params["index_name"] = params.pop("index_name")
|
||||
if "es_url" in params:
|
||||
existing_index_params["es_url"] = params.pop("es_url")
|
||||
|
||||
return class_object.from_existing_index(**existing_index_params)
|
||||
# If there are docs in the params, create a new index
|
||||
if "texts" in params:
|
||||
params["documents"] = params.pop("texts")
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
vecstore_initializer: Dict[str, Callable[[Type[Any], dict], Any]] = {
|
||||
"Pinecone": initialize_pinecone,
|
||||
"Chroma": initialize_chroma,
|
||||
"Qdrant": initialize_qdrant,
|
||||
"Weaviate": initialize_weaviate,
|
||||
"ElasticsearchStore": initialize_elasticsearch,
|
||||
"FAISS": initialize_faiss,
|
||||
"SupabaseVectorStore": initialize_supabase,
|
||||
"MongoDBAtlasVectorSearch": initialize_mongodb,
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
from typing import ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langchain import output_parsers
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode
|
||||
from langflow.utils.util import build_template_from_method
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class OutputParserCreator(LangChainTypeCreator):
|
||||
type_name: str = "output_parsers"
|
||||
from_method_nodes: ClassVar[Dict] = {
|
||||
"StructuredOutputParser": "from_response_schemas",
|
||||
}
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[OutputParserFrontendNode]:
|
||||
return OutputParserFrontendNode
|
||||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
if self.type_dict is None:
|
||||
settings_service = get_settings_service()
|
||||
self.type_dict = {
|
||||
output_parser_name: import_class(f"langchain.output_parsers.{output_parser_name}")
|
||||
# if output_parser_name is not lower case it is a class
|
||||
for output_parser_name in output_parsers.__all__
|
||||
}
|
||||
self.type_dict = {
|
||||
name: output_parser
|
||||
for name, output_parser in self.type_dict.items()
|
||||
if name in settings_service.settings.OUTPUT_PARSERS or settings_service.settings.DEV
|
||||
}
|
||||
return self.type_dict
|
||||
|
||||
def get_signature(self, name: str) -> Optional[Dict]:
|
||||
try:
|
||||
if name in self.from_method_nodes:
|
||||
return build_template_from_method(
|
||||
name,
|
||||
type_to_cls_dict=self.type_to_loader_dict,
|
||||
method_name=self.from_method_nodes[name],
|
||||
)
|
||||
else:
|
||||
return build_template_from_class(
|
||||
name,
|
||||
type_to_cls_dict=self.type_to_loader_dict,
|
||||
)
|
||||
except ValueError as exc:
|
||||
# raise ValueError("OutputParser not found") from exc
|
||||
logger.error(f"OutputParser {name} not found: {exc}")
|
||||
except AttributeError as exc:
|
||||
logger.error(f"OutputParser {name} not loaded: {exc}")
|
||||
return None
|
||||
|
||||
def to_list(self) -> List[str]:
|
||||
return list(self.type_to_loader_dict.keys())
|
||||
|
||||
|
||||
output_parser_creator = OutputParserCreator()
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
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.tools.json.tool import JsonSpec
|
||||
from langchain_community.tools.json.tool import JsonSpec
|
||||
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.interface.tools.custom import PythonFunctionTool
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ from langflow.interface.document_loaders.base import documentloader_creator
|
|||
from langflow.interface.embeddings.base import embedding_creator
|
||||
from langflow.interface.llms.base import llm_creator
|
||||
from langflow.interface.memories.base import memory_creator
|
||||
from langflow.interface.output_parsers.base import output_parser_creator
|
||||
from langflow.interface.retrievers.base import retriever_creator
|
||||
from langflow.interface.text_splitters.base import textsplitter_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
|
|
@ -48,7 +47,6 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union
|
|||
documentloader_creator,
|
||||
textsplitter_creator,
|
||||
# utility_creator,
|
||||
output_parser_creator,
|
||||
retriever_creator,
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ def set_langchain_cache(settings):
|
|||
|
||||
if cache_type := os.getenv("LANGFLOW_LANGCHAIN_CACHE"):
|
||||
try:
|
||||
cache_class = import_class(f"langchain.cache.{cache_type or settings.LANGCHAIN_CACHE}")
|
||||
cache_class = import_class(f"langchain_community.cache.{cache_type or settings.LANGCHAIN_CACHE}")
|
||||
|
||||
logger.debug(f"Setting up LLM caching with {cache_class.__name__}")
|
||||
set_llm_cache(cache_class())
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ def get_lifespan(fix_migration=False, socketio_server=None):
|
|||
except Exception as exc:
|
||||
if "langflow migration --fix" not in str(exc):
|
||||
logger.error(exc)
|
||||
raise
|
||||
# Shutdown message
|
||||
rprint("[bold red]Shutting down Langflow...[/bold red]")
|
||||
teardown_services()
|
||||
|
|
|
|||
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