Merge remote-tracking branch 'origin/dev' into node-shortcuts-refactor
This commit is contained in:
commit
23bcdcec28
216 changed files with 11018 additions and 10942 deletions
|
|
@ -9,11 +9,6 @@ import click
|
|||
import httpx
|
||||
import typer
|
||||
from dotenv import load_dotenv
|
||||
from langflow.main import setup_app
|
||||
from langflow.services.database.utils import session_getter
|
||||
from langflow.services.deps import get_db_service, get_settings_service
|
||||
from langflow.services.utils import initialize_services, initialize_settings_service
|
||||
from langflow.utils.logger import configure, logger
|
||||
from multiprocess import Process, cpu_count # type: ignore
|
||||
from packaging import version as pkg_version
|
||||
from rich import box
|
||||
|
|
@ -22,6 +17,13 @@ from rich.console import Console
|
|||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
|
||||
from langflow.main import setup_app
|
||||
from langflow.services.database.utils import session_getter
|
||||
from langflow.services.deps import get_db_service
|
||||
from langflow.services.utils import initialize_services
|
||||
from langflow.utils.logger import configure, logger
|
||||
from langflow.utils.util import update_settings
|
||||
|
||||
console = Console()
|
||||
|
||||
app = typer.Typer(no_args_is_help=True)
|
||||
|
|
@ -67,44 +69,10 @@ def set_var_for_macos_issue():
|
|||
logger.debug("Set OBJC_DISABLE_INITIALIZE_FORK_SAFETY to YES to avoid error")
|
||||
|
||||
|
||||
def update_settings(
|
||||
config: str,
|
||||
cache: Optional[str] = None,
|
||||
dev: bool = False,
|
||||
remove_api_keys: bool = False,
|
||||
components_path: Optional[Path] = None,
|
||||
store: bool = True,
|
||||
):
|
||||
"""Update the settings from a config file."""
|
||||
|
||||
# Check for database_url in the environment variables
|
||||
initialize_settings_service()
|
||||
settings_service = get_settings_service()
|
||||
if config:
|
||||
logger.debug(f"Loading settings from {config}")
|
||||
settings_service.settings.update_from_yaml(config, dev=dev)
|
||||
if remove_api_keys:
|
||||
logger.debug(f"Setting remove_api_keys to {remove_api_keys}")
|
||||
settings_service.settings.update_settings(REMOVE_API_KEYS=remove_api_keys)
|
||||
if cache:
|
||||
logger.debug(f"Setting cache to {cache}")
|
||||
settings_service.settings.update_settings(CACHE=cache)
|
||||
if components_path:
|
||||
logger.debug(f"Adding component path {components_path}")
|
||||
settings_service.settings.update_settings(COMPONENTS_PATH=components_path)
|
||||
if not store:
|
||||
logger.debug("Setting store to False")
|
||||
settings_service.settings.update_settings(STORE=False)
|
||||
|
||||
|
||||
@app.command()
|
||||
def run(
|
||||
host: str = typer.Option(
|
||||
"127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"
|
||||
),
|
||||
workers: int = typer.Option(
|
||||
1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"
|
||||
),
|
||||
host: str = typer.Option("127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"),
|
||||
workers: int = typer.Option(1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"),
|
||||
timeout: int = typer.Option(300, help="Worker timeout in seconds."),
|
||||
port: int = typer.Option(7860, help="Port to listen on.", envvar="LANGFLOW_PORT"),
|
||||
components_path: Optional[Path] = typer.Option(
|
||||
|
|
@ -112,19 +80,11 @@ def run(
|
|||
help="Path to the directory containing custom components.",
|
||||
envvar="LANGFLOW_COMPONENTS_PATH",
|
||||
),
|
||||
config: str = typer.Option(
|
||||
Path(__file__).parent / "config.yaml", help="Path to the configuration file."
|
||||
),
|
||||
config: str = typer.Option(Path(__file__).parent / "config.yaml", help="Path to the configuration file."),
|
||||
# .env file param
|
||||
env_file: Path = typer.Option(
|
||||
None, help="Path to the .env file containing environment variables."
|
||||
),
|
||||
log_level: str = typer.Option(
|
||||
"critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
|
||||
),
|
||||
log_file: Path = typer.Option(
|
||||
"logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"
|
||||
),
|
||||
env_file: Path = typer.Option(None, help="Path to the .env file containing environment variables."),
|
||||
log_level: str = typer.Option("critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"),
|
||||
log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"),
|
||||
cache: Optional[str] = typer.Option(
|
||||
envvar="LANGFLOW_LANGCHAIN_CACHE",
|
||||
help="Type of cache to use. (InMemoryCache, SQLiteCache)",
|
||||
|
|
@ -192,17 +152,21 @@ def run(
|
|||
# Define an env variable to know if we are just testing the server
|
||||
if "pytest" in sys.modules:
|
||||
return
|
||||
|
||||
if platform.system() in ["Windows"]:
|
||||
# Run using uvicorn on MacOS and Windows
|
||||
# Windows doesn't support gunicorn
|
||||
# MacOS requires an env variable to be set to use gunicorn
|
||||
run_on_windows(host, port, log_level, options, app)
|
||||
else:
|
||||
# Run using gunicorn on Linux
|
||||
run_on_mac_or_linux(host, port, log_level, options, app, open_browser)
|
||||
if open_browser:
|
||||
click.launch(f"http://{host}:{port}")
|
||||
try:
|
||||
if platform.system() in ["Windows"]:
|
||||
# Run using uvicorn on MacOS and Windows
|
||||
# Windows doesn't support gunicorn
|
||||
# MacOS requires an env variable to be set to use gunicorn
|
||||
process = run_on_windows(host, port, log_level, options, app)
|
||||
else:
|
||||
# Run using gunicorn on Linux
|
||||
process = run_on_mac_or_linux(host, port, log_level, options, app)
|
||||
if open_browser:
|
||||
click.launch(f"http://{host}:{port}")
|
||||
if process:
|
||||
process.join()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
||||
def wait_for_server_ready(host, port):
|
||||
|
|
@ -218,13 +182,12 @@ def wait_for_server_ready(host, port):
|
|||
|
||||
|
||||
def run_on_mac_or_linux(host, port, log_level, options, app):
|
||||
webapp_process = Process(
|
||||
target=run_langflow, args=(host, port, log_level, options, app)
|
||||
)
|
||||
webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app))
|
||||
webapp_process.start()
|
||||
wait_for_server_ready(host, port)
|
||||
|
||||
print_banner(host, port)
|
||||
return webapp_process
|
||||
|
||||
|
||||
def run_on_windows(host, port, log_level, options, app):
|
||||
|
|
@ -233,6 +196,7 @@ def run_on_windows(host, port, log_level, options, app):
|
|||
"""
|
||||
print_banner(host, port)
|
||||
run_langflow(host, port, log_level, options, app)
|
||||
return None
|
||||
|
||||
|
||||
def is_port_in_use(port, host="localhost"):
|
||||
|
|
@ -316,9 +280,7 @@ def build_new_version_notice(current_version: str, package_name: str):
|
|||
f"A new pre-release version of {package_name} is available: {latest_version}",
|
||||
)
|
||||
else:
|
||||
latest_version = httpx.get(f"https://pypi.org/pypi/{package_name}/json").json()[
|
||||
"info"
|
||||
]["version"]
|
||||
latest_version = httpx.get(f"https://pypi.org/pypi/{package_name}/json").json()["info"]["version"]
|
||||
if not version_is_prerelease(latest_version):
|
||||
return (
|
||||
False,
|
||||
|
|
@ -331,7 +293,7 @@ def is_prerelease(version: str) -> bool:
|
|||
return "a" in version or "b" in version or "rc" in version
|
||||
|
||||
|
||||
def fetch_latest_version(package_name: str, include_prerelease: bool) -> str:
|
||||
def fetch_latest_version(package_name: str, include_prerelease: bool) -> Optional[str]:
|
||||
response = httpx.get(f"https://pypi.org/pypi/{package_name}/json")
|
||||
versions = response.json()["releases"].keys()
|
||||
valid_versions = [v for v in versions if include_prerelease or not is_prerelease(v)]
|
||||
|
|
@ -342,9 +304,7 @@ def fetch_latest_version(package_name: str, include_prerelease: bool) -> str:
|
|||
|
||||
def build_version_notice(current_version: str, package_name: str) -> str:
|
||||
latest_version = fetch_latest_version(package_name, is_prerelease(current_version))
|
||||
if latest_version and pkg_version.parse(current_version) < pkg_version.parse(
|
||||
latest_version
|
||||
):
|
||||
if latest_version and pkg_version.parse(current_version) < pkg_version.parse(latest_version):
|
||||
release_type = "pre-release" if is_prerelease(latest_version) else "version"
|
||||
return f"A new {release_type} of {package_name} is available: {latest_version}"
|
||||
return ""
|
||||
|
|
@ -375,7 +335,7 @@ def print_banner(host: str, port: int):
|
|||
package_name = ""
|
||||
|
||||
try:
|
||||
from langflow.version import __version__ as langflow_version
|
||||
from langflow.version import __version__ as langflow_version # type: ignore
|
||||
|
||||
is_pre_release |= is_prerelease(langflow_version) # Update pre-release status
|
||||
notice = build_version_notice(langflow_version, "langflow")
|
||||
|
|
@ -393,9 +353,7 @@ def print_banner(host: str, port: int):
|
|||
from importlib import metadata
|
||||
|
||||
langflow_base_version = metadata.version("langflow-base")
|
||||
is_pre_release |= is_prerelease(
|
||||
langflow_base_version
|
||||
) # Update pre-release status
|
||||
is_pre_release |= is_prerelease(langflow_base_version) # Update pre-release status
|
||||
notice = build_version_notice(langflow_base_version, "langflow-base")
|
||||
notice = stylize_text(notice, "langflow-base", is_pre_release)
|
||||
if notice:
|
||||
|
|
@ -414,12 +372,10 @@ def print_banner(host: str, port: int):
|
|||
notices.append(f"Run '{pip_command}' to update.")
|
||||
|
||||
styled_notices = [f"[bold]{notice}[/bold]" for notice in notices if notice]
|
||||
styled_package_name = stylize_text(
|
||||
package_name, package_name, any("pre-release" in notice for notice in notices)
|
||||
)
|
||||
styled_package_name = stylize_text(package_name, package_name, any("pre-release" in notice for notice in notices))
|
||||
|
||||
title = f"[bold]Welcome to :chains: {styled_package_name}[/bold]\n"
|
||||
info_text = "Collaborate, and contribute at our [bold][link=https://github.com/logspace-ai/langflow]GitHub Repo[/link][/bold] :rocket:"
|
||||
info_text = "Collaborate, and contribute at our [bold][link=https://github.com/langflow-ai/langflow]GitHub Repo[/link][/bold] :rocket:"
|
||||
access_link = f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]"
|
||||
|
||||
panel_content = "\n\n".join([title, *styled_notices, info_text, access_link])
|
||||
|
|
@ -459,12 +415,8 @@ def run_langflow(host, port, log_level, options, app):
|
|||
@app.command()
|
||||
def superuser(
|
||||
username: str = typer.Option(..., prompt=True, help="Username for the superuser."),
|
||||
password: str = typer.Option(
|
||||
..., prompt=True, hide_input=True, help="Password for the superuser."
|
||||
),
|
||||
log_level: str = typer.Option(
|
||||
"error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
|
||||
),
|
||||
password: str = typer.Option(..., prompt=True, hide_input=True, help="Password for the superuser."),
|
||||
log_level: str = typer.Option("error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"),
|
||||
):
|
||||
"""
|
||||
Create a superuser.
|
||||
|
|
@ -491,11 +443,23 @@ def superuser(
|
|||
|
||||
|
||||
@app.command()
|
||||
def migration(test: bool = typer.Option(True, help="Run migrations in test mode.")):
|
||||
def migration(
|
||||
test: bool = typer.Option(True, help="Run migrations in test mode."),
|
||||
fix: bool = typer.Option(
|
||||
False,
|
||||
help="Fix migrations. This is a destructive operation, and should only be used if you know what you are doing.",
|
||||
),
|
||||
):
|
||||
"""
|
||||
Run or test migrations.
|
||||
"""
|
||||
initialize_services()
|
||||
if fix:
|
||||
if not typer.confirm(
|
||||
"This will delete all data necessary to fix migrations. Are you sure you want to continue?"
|
||||
):
|
||||
raise typer.Abort()
|
||||
|
||||
initialize_services(fix_migration=fix)
|
||||
db_service = get_db_service()
|
||||
if not test:
|
||||
db_service.run_migrations()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
"""Change datetime type
|
||||
|
||||
Revision ID: 79e675cb6752
|
||||
Revises: e3bc869fa272
|
||||
Create Date: 2024-04-11 19:23:10.697335
|
||||
|
||||
"""
|
||||
from calendar import c
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "79e675cb6752"
|
||||
down_revision: Union[str, None] = "e3bc869fa272"
|
||||
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 "apikey" in table_names:
|
||||
columns = inspector.get_columns("apikey")
|
||||
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
|
||||
if created_at_column is not None and created_at_column["type"] == postgresql.TIMESTAMP():
|
||||
with op.batch_alter_table("apikey", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=False,
|
||||
)
|
||||
if "variable" in table_names:
|
||||
columns = inspector.get_columns("variable")
|
||||
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
|
||||
updated_at_column = next((column for column in columns if column["name"] == "updated_at"), None)
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if created_at_column is not None and created_at_column["type"] == postgresql.TIMESTAMP():
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
)
|
||||
if updated_at_column is not None and updated_at_column["type"] == postgresql.TIMESTAMP():
|
||||
batch_op.alter_column(
|
||||
"updated_at",
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
# ### 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 "variable" in table_names:
|
||||
columns = inspector.get_columns("variable")
|
||||
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
|
||||
updated_at_column = next((column for column in columns if column["name"] == "updated_at"), None)
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if updated_at_column is not None and updated_at_column["type"] == sa.DateTime(timezone=True):
|
||||
batch_op.alter_column(
|
||||
"updated_at",
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
if created_at_column is not None and created_at_column["type"] == sa.DateTime(timezone=True):
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
if "apikey" in table_names:
|
||||
columns = inspector.get_columns("apikey")
|
||||
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
|
||||
if created_at_column is not None and created_at_column["type"] == sa.DateTime(timezone=True):
|
||||
with op.batch_alter_table("apikey", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
"""Fix nullable
|
||||
|
||||
Revision ID: e3bc869fa272
|
||||
Revises: 1a110b568907
|
||||
Create Date: 2024-04-10 19:17:22.820455
|
||||
|
||||
"""
|
||||
|
||||
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 = "e3bc869fa272"
|
||||
down_revision: Union[str, None] = "1a110b568907"
|
||||
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 "variable" not in table_names:
|
||||
return
|
||||
|
||||
column_names = [column["name"] for column in inspector.get_columns("variable")]
|
||||
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if "created_at" in column_names:
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=sa.TIMESTAMP(timezone=True),
|
||||
nullable=True,
|
||||
# existing_server_default expects str | bool | Identity | Computed | None
|
||||
# sa.text("now()") is not a valid value for existing_server_default
|
||||
existing_server_default=False,
|
||||
)
|
||||
|
||||
# ### 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 "variable" not in table_names:
|
||||
return
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if "created_at" in inspector.get_columns("variable"):
|
||||
batch_op.alter_column(
|
||||
"created_at",
|
||||
existing_type=sa.TIMESTAMP(timezone=True),
|
||||
nullable=False,
|
||||
existing_server_default=False,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -14,7 +14,6 @@ from langflow.api.v1.schemas import (
|
|||
RunResponse,
|
||||
SimplifiedAPIRequest,
|
||||
TaskStatusResponse,
|
||||
Tweaks,
|
||||
UpdateCustomComponentRequest,
|
||||
UploadFileResponse,
|
||||
)
|
||||
|
|
@ -24,6 +23,7 @@ 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
|
||||
from langflow.services.auth.utils import api_key_security, get_current_active_user
|
||||
from langflow.services.cache.utils import save_uploaded_file
|
||||
from langflow.services.database.models.flow import Flow
|
||||
|
|
@ -373,7 +373,7 @@ async def create_upload_file(
|
|||
@router.get("/version")
|
||||
def get_version():
|
||||
try:
|
||||
from langflow.version import __version__
|
||||
from langflow.version import __version__ # type: ignore
|
||||
|
||||
version = __version__
|
||||
package = "Langflow"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
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,
|
||||
|
|
@ -9,8 +7,14 @@ from langflow.services.auth.utils import (
|
|||
create_user_longterm_token,
|
||||
create_user_tokens,
|
||||
)
|
||||
from langflow.services.deps import get_session, get_settings_service
|
||||
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"])
|
||||
|
||||
|
|
@ -22,6 +26,7 @@ async def login_to_get_access_token(
|
|||
db: Session = Depends(get_session),
|
||||
# _: Session = Depends(get_current_active_user)
|
||||
settings_service=Depends(get_settings_service),
|
||||
variable_service: VariableService = Depends(get_variable_service),
|
||||
):
|
||||
auth_settings = settings_service.auth_settings
|
||||
try:
|
||||
|
|
@ -52,6 +57,7 @@ async def login_to_get_access_token(
|
|||
secure=auth_settings.ACCESS_SECURE,
|
||||
expires=auth_settings.ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||
)
|
||||
variable_service.initialize_user_variables(user.id, db)
|
||||
return tokens
|
||||
else:
|
||||
raise HTTPException(
|
||||
|
|
@ -66,10 +72,11 @@ async def auto_login(
|
|||
response: Response,
|
||||
db: Session = Depends(get_session),
|
||||
settings_service=Depends(get_settings_service),
|
||||
variable_service: VariableService = Depends(get_variable_service),
|
||||
):
|
||||
auth_settings = settings_service.auth_settings
|
||||
if settings_service.auth_settings.AUTO_LOGIN:
|
||||
tokens = create_user_longterm_token(db)
|
||||
user_id, tokens = create_user_longterm_token(db)
|
||||
response.set_cookie(
|
||||
"access_token_lf",
|
||||
tokens["access_token"],
|
||||
|
|
@ -78,6 +85,7 @@ async def auto_login(
|
|||
secure=auth_settings.ACCESS_SECURE,
|
||||
expires=None, # Set to None to make it a session cookie
|
||||
)
|
||||
variable_service.initialize_user_variables(user_id, db)
|
||||
return tokens
|
||||
|
||||
raise HTTPException(
|
||||
|
|
@ -91,7 +99,9 @@ async def auto_login(
|
|||
|
||||
@router.post("/refresh")
|
||||
async def refresh_token(
|
||||
request: Request, response: Response, settings_service: "SettingsService" = Depends(get_settings_service)
|
||||
request: Request,
|
||||
response: Response,
|
||||
settings_service: "SettingsService" = Depends(get_settings_service),
|
||||
):
|
||||
auth_settings = settings_service.auth_settings
|
||||
|
||||
|
|
@ -129,3 +139,4 @@ 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"}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ from pathlib import Path
|
|||
from typing import Any, Dict, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_serializer
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_serializer
|
||||
|
||||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.schema import dotdict
|
||||
from langflow.schema.graph import Tweaks
|
||||
from langflow.schema.schema import InputType, OutputType
|
||||
from langflow.services.database.models.api_key.model import ApiKeyRead
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
|
@ -283,36 +284,6 @@ class InputValueRequest(BaseModel):
|
|||
)
|
||||
|
||||
|
||||
class Tweaks(RootModel):
|
||||
root: dict[str, Union[str, dict[str, str]]] = Field(
|
||||
description="A dictionary of tweaks to adjust the flow's execution. Allows customizing flow behavior dynamically. All tweaks are overridden by the input values.",
|
||||
)
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"parameter_name": "value",
|
||||
"Component Name": {"parameter_name": "value"},
|
||||
"component_id": {"parameter_name": "value"},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# This should behave like a dict
|
||||
def __getitem__(self, key):
|
||||
return self.root[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.root[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.root[key]
|
||||
|
||||
def items(self):
|
||||
return self.root.items()
|
||||
|
||||
|
||||
class SimplifiedAPIRequest(BaseModel):
|
||||
input_value: Optional[str] = Field(default="", description="The input value")
|
||||
input_type: Optional[InputType] = Field(default="chat", description="The input type")
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from fastapi import APIRouter, HTTPException
|
|||
from loguru import logger
|
||||
|
||||
from langflow.api.v1.base import Code, CodeValidationResponse, PromptValidationResponse, ValidatePromptRequest
|
||||
from langflow.base.prompts.utils import (
|
||||
from langflow.base.prompts.api_utils import (
|
||||
add_new_variables_to_template,
|
||||
get_old_custom_fields,
|
||||
remove_old_variables_from_template,
|
||||
|
|
|
|||
|
|
@ -24,4 +24,5 @@ FIELD_FORMAT_ATTRIBUTES = [
|
|||
"real_time_refresh",
|
||||
"refresh_button",
|
||||
"refresh_button_text",
|
||||
"options",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,26 @@ from langflow.schema.schema import Record
|
|||
|
||||
# Types of files that can be read simply by file.read()
|
||||
# and have 100% to be completely readable
|
||||
TEXT_FILE_TYPES = ["txt", "md", "mdx", "csv", "json", "yaml", "yml", "xml", "html", "htm", "pdf", "docx"]
|
||||
TEXT_FILE_TYPES = [
|
||||
"txt",
|
||||
"md",
|
||||
"mdx",
|
||||
"csv",
|
||||
"json",
|
||||
"yaml",
|
||||
"yml",
|
||||
"xml",
|
||||
"html",
|
||||
"htm",
|
||||
"pdf",
|
||||
"docx",
|
||||
"py",
|
||||
"sh",
|
||||
"sql",
|
||||
"js",
|
||||
"ts",
|
||||
"tsx",
|
||||
]
|
||||
|
||||
|
||||
def is_hidden(path: Path) -> bool:
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class ChatComponent(CustomComponent):
|
|||
records = add_messages([record])
|
||||
return records[0]
|
||||
|
||||
def build(
|
||||
def build_with_record(
|
||||
self,
|
||||
sender: Optional[str] = "User",
|
||||
sender_name: Optional[str] = "User",
|
||||
|
|
@ -116,3 +116,41 @@ class ChatComponent(CustomComponent):
|
|||
if session_id and isinstance(result, (Record, str)):
|
||||
self.store_message(result, session_id, sender, sender_name)
|
||||
return result
|
||||
|
||||
def build_no_record(
|
||||
self,
|
||||
sender: Optional[str] = "User",
|
||||
sender_name: Optional[str] = "User",
|
||||
input_value: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
record_template: str = "Text: {text}\nData: {data}",
|
||||
) -> Union[Text, Record]:
|
||||
input_value_record: Optional[Record] = None
|
||||
if return_record:
|
||||
if isinstance(input_value, Record):
|
||||
# Update the data of the record
|
||||
input_value.data["sender"] = sender
|
||||
input_value.data["sender_name"] = sender_name
|
||||
input_value.data["session_id"] = session_id
|
||||
else:
|
||||
input_value_record = Record(
|
||||
text=input_value,
|
||||
data={
|
||||
"sender": sender,
|
||||
"sender_name": sender_name,
|
||||
"session_id": session_id,
|
||||
},
|
||||
)
|
||||
elif isinstance(input_value, Record):
|
||||
input_value = records_to_text(template=record_template, records=input_value)
|
||||
if not input_value:
|
||||
input_value = ""
|
||||
if return_record and input_value_record:
|
||||
result: Union[Text, Record] = input_value_record
|
||||
else:
|
||||
result = input_value
|
||||
self.status = result
|
||||
if session_id and isinstance(result, (Record, str)):
|
||||
self.store_message(result, session_id, sender, sender_name)
|
||||
return result
|
||||
|
|
|
|||
83
src/backend/base/langflow/base/prompts/api_utils.py
Normal file
83
src/backend/base/langflow/base/prompts/api_utils.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from fastapi import HTTPException
|
||||
from langchain.prompts import PromptTemplate
|
||||
from loguru import logger
|
||||
|
||||
from langflow.api.v1.base import INVALID_NAMES, check_input_variables
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langflow.template.field.prompt import DefaultPromptField
|
||||
|
||||
|
||||
def validate_prompt(prompt_template: str, silent_errors: bool = False) -> list[str]:
|
||||
input_variables = extract_input_variables_from_prompt(prompt_template)
|
||||
|
||||
# Check if there are invalid characters in the input_variables
|
||||
input_variables = check_input_variables(input_variables)
|
||||
if any(var in INVALID_NAMES for var in input_variables):
|
||||
raise ValueError(f"Invalid input variables. None of the variables can be named {', '.join(input_variables)}. ")
|
||||
|
||||
try:
|
||||
PromptTemplate(template=prompt_template, input_variables=input_variables)
|
||||
except Exception as exc:
|
||||
logger.error(f"Invalid prompt: {exc}")
|
||||
if not silent_errors:
|
||||
raise ValueError(f"Invalid prompt: {exc}") from exc
|
||||
|
||||
return input_variables
|
||||
|
||||
|
||||
def get_old_custom_fields(custom_fields, name):
|
||||
try:
|
||||
if len(custom_fields) == 1 and name == "":
|
||||
# If there is only one custom field and the name is empty string
|
||||
# then we are dealing with the first prompt request after the node was created
|
||||
name = list(custom_fields.keys())[0]
|
||||
|
||||
old_custom_fields = custom_fields[name]
|
||||
if not old_custom_fields:
|
||||
old_custom_fields = []
|
||||
|
||||
old_custom_fields = old_custom_fields.copy()
|
||||
except KeyError:
|
||||
old_custom_fields = []
|
||||
custom_fields[name] = []
|
||||
return old_custom_fields
|
||||
|
||||
|
||||
def add_new_variables_to_template(input_variables, custom_fields, template, name):
|
||||
for variable in input_variables:
|
||||
try:
|
||||
template_field = DefaultPromptField(name=variable, display_name=variable)
|
||||
if variable in template:
|
||||
# Set the new field with the old value
|
||||
template_field.value = template[variable]["value"]
|
||||
|
||||
template[variable] = template_field.to_dict()
|
||||
|
||||
# Check if variable is not already in the list before appending
|
||||
if variable not in custom_fields[name]:
|
||||
custom_fields[name].append(variable)
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
||||
|
||||
def remove_old_variables_from_template(old_custom_fields, input_variables, custom_fields, template, name):
|
||||
for variable in old_custom_fields:
|
||||
if variable not in input_variables:
|
||||
try:
|
||||
# Remove the variable from custom_fields associated with the given name
|
||||
if variable in custom_fields[name]:
|
||||
custom_fields[name].remove(variable)
|
||||
|
||||
# Remove the variable from the template
|
||||
template.pop(variable, None)
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
||||
|
||||
def update_input_variables_field(input_variables, template):
|
||||
if "input_variables" in template:
|
||||
template["input_variables"]["value"] = input_variables
|
||||
|
|
@ -1,12 +1,19 @@
|
|||
from fastapi import HTTPException
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain_core.documents import Document
|
||||
from loguru import logger
|
||||
|
||||
from langflow.api.v1.base import INVALID_NAMES, check_input_variables
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langflow.schema import Record
|
||||
from langflow.template.field.prompt import DefaultPromptField
|
||||
|
||||
|
||||
def record_to_string(record: Record) -> str:
|
||||
"""
|
||||
Convert a record to a string.
|
||||
|
||||
Args:
|
||||
record (Record): The record to convert.
|
||||
|
||||
Returns:
|
||||
str: The record as a string.
|
||||
"""
|
||||
return record.get_text()
|
||||
|
||||
|
||||
def dict_values_to_string(d: dict) -> dict:
|
||||
|
|
@ -35,19 +42,6 @@ def dict_values_to_string(d: dict) -> dict:
|
|||
return d
|
||||
|
||||
|
||||
def record_to_string(record: Record) -> str:
|
||||
"""
|
||||
Convert a record to a string.
|
||||
|
||||
Args:
|
||||
record (Record): The record to convert.
|
||||
|
||||
Returns:
|
||||
str: The record as a string.
|
||||
"""
|
||||
return record.get_text()
|
||||
|
||||
|
||||
def document_to_string(document: Document) -> str:
|
||||
"""
|
||||
Convert a document to a string.
|
||||
|
|
@ -59,79 +53,3 @@ def document_to_string(document: Document) -> str:
|
|||
str: The document as a string.
|
||||
"""
|
||||
return document.page_content
|
||||
|
||||
|
||||
def validate_prompt(prompt_template: str, silent_errors: bool = False) -> list[str]:
|
||||
input_variables = extract_input_variables_from_prompt(prompt_template)
|
||||
|
||||
# Check if there are invalid characters in the input_variables
|
||||
input_variables = check_input_variables(input_variables)
|
||||
if any(var in INVALID_NAMES for var in input_variables):
|
||||
raise ValueError(f"Invalid input variables. None of the variables can be named {', '.join(input_variables)}. ")
|
||||
|
||||
try:
|
||||
PromptTemplate(template=prompt_template, input_variables=input_variables)
|
||||
except Exception as exc:
|
||||
logger.error(f"Invalid prompt: {exc}")
|
||||
if not silent_errors:
|
||||
raise ValueError(f"Invalid prompt: {exc}") from exc
|
||||
|
||||
return input_variables
|
||||
|
||||
|
||||
def get_old_custom_fields(custom_fields, name):
|
||||
try:
|
||||
if len(custom_fields) == 1 and name == "":
|
||||
# If there is only one custom field and the name is empty string
|
||||
# then we are dealing with the first prompt request after the node was created
|
||||
name = list(custom_fields.keys())[0]
|
||||
|
||||
old_custom_fields = custom_fields[name]
|
||||
if not old_custom_fields:
|
||||
old_custom_fields = []
|
||||
|
||||
old_custom_fields = old_custom_fields.copy()
|
||||
except KeyError:
|
||||
old_custom_fields = []
|
||||
custom_fields[name] = []
|
||||
return old_custom_fields
|
||||
|
||||
|
||||
def add_new_variables_to_template(input_variables, custom_fields, template, name):
|
||||
for variable in input_variables:
|
||||
try:
|
||||
template_field = DefaultPromptField(name=variable, display_name=variable)
|
||||
if variable in template:
|
||||
# Set the new field with the old value
|
||||
template_field.value = template[variable]["value"]
|
||||
|
||||
template[variable] = template_field.to_dict()
|
||||
|
||||
# Check if variable is not already in the list before appending
|
||||
if variable not in custom_fields[name]:
|
||||
custom_fields[name].append(variable)
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
||||
|
||||
def remove_old_variables_from_template(old_custom_fields, input_variables, custom_fields, template, name):
|
||||
for variable in old_custom_fields:
|
||||
if variable not in input_variables:
|
||||
try:
|
||||
# Remove the variable from custom_fields associated with the given name
|
||||
if variable in custom_fields[name]:
|
||||
custom_fields[name].remove(variable)
|
||||
|
||||
# Remove the variable from the template
|
||||
template.pop(variable, None)
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||
|
||||
|
||||
def update_input_variables_field(input_variables, template):
|
||||
if "input_variables" in template:
|
||||
template["input_variables"]["value"] = input_variables
|
||||
|
|
|
|||
0
src/backend/base/langflow/base/tools/__init__.py
Normal file
0
src/backend/base/langflow/base/tools/__init__.py
Normal file
23
src/backend/base/langflow/base/tools/base.py
Normal file
23
src/backend/base/langflow/base/tools/base.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from langflow.field_typing import Tool
|
||||
|
||||
|
||||
def build_status_from_tool(tool: Tool):
|
||||
"""
|
||||
Builds a status string representation of a tool.
|
||||
|
||||
Args:
|
||||
tool (Tool): The tool object to build the status for.
|
||||
|
||||
Returns:
|
||||
str: The status string representation of the tool, including its name, description, and arguments (if any).
|
||||
"""
|
||||
description_repr = repr(tool.description).strip("'")
|
||||
args_str = "\n".join(
|
||||
[
|
||||
f"- {arg_name}: {arg_data['description']}"
|
||||
for arg_name, arg_data in tool.args.items()
|
||||
if "description" in arg_data
|
||||
]
|
||||
)
|
||||
status = f"Name: {tool.name}\nDescription: {description_repr}"
|
||||
return status + (f"\nArguments:\n{args_str}" if args_str else "")
|
||||
|
|
@ -4,7 +4,7 @@ from langchain.agents import create_xml_agent
|
|||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.field_typing import BaseLLM, BaseMemory, Text, Tool
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text, Tool
|
||||
|
||||
|
||||
class XMLAgentComponent(LCAgentComponent):
|
||||
|
|
@ -66,7 +66,7 @@ class XMLAgentComponent(LCAgentComponent):
|
|||
async def build(
|
||||
self,
|
||||
input_value: str,
|
||||
llm: BaseLLM,
|
||||
llm: BaseLanguageModel,
|
||||
tools: List[Tool],
|
||||
prompt: str,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.chains import LLMChain
|
||||
from langchain.chains.llm import LLMChain
|
||||
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, BasePromptTemplate, Text
|
||||
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
|
||||
class LLMChainComponent(CustomComponent):
|
||||
|
|
@ -19,10 +20,11 @@ class LLMChainComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
prompt: BasePromptTemplate,
|
||||
template: Text,
|
||||
llm: BaseLanguageModel,
|
||||
memory: Optional[BaseMemory] = None,
|
||||
) -> Text:
|
||||
prompt = PromptTemplate.from_template(template)
|
||||
runnable = LLMChain(prompt=prompt, llm=llm, memory=memory)
|
||||
result_dict = runnable.invoke({})
|
||||
output_key = runnable.output_key
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import json
|
|||
from typing import List, Optional
|
||||
|
||||
import httpx
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema import Record
|
||||
|
||||
|
|
@ -47,8 +46,8 @@ class APIRequest(CustomComponent):
|
|||
client: httpx.AsyncClient,
|
||||
method: str,
|
||||
url: str,
|
||||
headers: Optional[Record] = None,
|
||||
body: Optional[Record] = None,
|
||||
headers: Optional[dict] = None,
|
||||
body: Optional[dict] = None,
|
||||
timeout: int = 5,
|
||||
) -> Record:
|
||||
method = method.upper()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from langchain_openai.embeddings.base import OpenAIEmbeddings
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.field_typing import Embeddings, NestedDict
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
@ -113,6 +114,10 @@ class OpenAIEmbeddingsComponent(CustomComponent):
|
|||
# This is to avoid errors with Vector Stores (e.g Chroma)
|
||||
if disallowed_special == ["all"]:
|
||||
disallowed_special = "all" # type: ignore
|
||||
if openai_api_key:
|
||||
api_key = SecretStr(openai_api_key)
|
||||
else:
|
||||
api_key = None
|
||||
|
||||
return OpenAIEmbeddings(
|
||||
tiktoken_enabled=tiktoken_enable,
|
||||
|
|
@ -128,7 +133,7 @@ class OpenAIEmbeddingsComponent(CustomComponent):
|
|||
model=model,
|
||||
model_kwargs=model_kwargs,
|
||||
base_url=openai_api_base,
|
||||
api_key=openai_api_key,
|
||||
api_key=api_key,
|
||||
openai_api_type=openai_api_type,
|
||||
api_version=openai_api_version,
|
||||
organization=openai_organization,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
from typing import Any, List, Optional
|
||||
|
||||
from asyncer import syncify
|
||||
from langchain_core.tools import StructuredTool
|
||||
from loguru import logger
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Tool
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.helpers.flow import build_function_and_schema
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.schema.schema import Record
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class FlowToolComponent(CustomComponent):
|
||||
|
|
@ -74,6 +74,7 @@ class FlowToolComponent(CustomComponent):
|
|||
graph = Graph.from_payload(flow_record.data["data"])
|
||||
dynamic_flow_function, schema = build_function_and_schema(flow_record, graph)
|
||||
tool = StructuredTool.from_function(
|
||||
func=syncify(dynamic_flow_function, raise_sync_error=False), # type: ignore
|
||||
coroutine=dynamic_flow_function,
|
||||
name=name,
|
||||
description=description,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,6 @@ class MemoryComponent(CustomComponent):
|
|||
limit=n_messages,
|
||||
order=order,
|
||||
)
|
||||
messages_str = records_to_text(template=record_template, records=messages)
|
||||
messages_str = records_to_text(template=record_template or "", records=messages)
|
||||
self.status = messages_str
|
||||
return messages_str
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter
|
||||
from langchain_core.documents import Document
|
||||
|
|
@ -51,6 +51,8 @@ class SplitTextComponent(CustomComponent):
|
|||
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
|
||||
|
|
@ -58,7 +60,7 @@ class SplitTextComponent(CustomComponent):
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class ChatInput(ChatComponent):
|
|||
session_id: Optional[str] = None,
|
||||
return_record: Optional[bool] = False,
|
||||
) -> Union[Text, Record]:
|
||||
return super().build(
|
||||
return super().build_no_record(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
input_value=input_value,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class TextInput(TextComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
input_value: Optional[str] = "",
|
||||
input_value: Optional[Text] = "",
|
||||
record_template: Optional[str] = "",
|
||||
) -> Text:
|
||||
return super().build(input_value=input_value, record_template=record_template)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain_community.llms.bedrock import Bedrock
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
@ -46,7 +45,7 @@ class AmazonBedrockComponent(CustomComponent):
|
|||
endpoint_url: Optional[str] = None,
|
||||
streaming: bool = False,
|
||||
cache: Optional[bool] = None,
|
||||
) -> BaseLLM:
|
||||
) -> BaseLanguageModel:
|
||||
try:
|
||||
output = Bedrock(
|
||||
credentials_profile_name=credentials_profile_name,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLanguageModel
|
||||
from langchain_community.chat_models.anthropic import ChatAnthropic
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
||||
class AnthropicLLM(CustomComponent):
|
||||
display_name: str = "AnthropicLLM"
|
||||
class ChatAntropicSpecsComponent(CustomComponent):
|
||||
display_name: str = "Anthropic"
|
||||
description: str = "Anthropic Chat&Completion large language models."
|
||||
icon = "Anthropic"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_community.llms.anthropic import Anthropic
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.field_typing import BaseLanguageModel, NestedDict
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
||||
class AnthropicComponent(CustomComponent):
|
||||
display_name = "Anthropic"
|
||||
description = "Anthropic large language models."
|
||||
icon = "Anthropic"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"anthropic_api_key": {
|
||||
"display_name": "Anthropic API Key",
|
||||
"type": str,
|
||||
"password": True,
|
||||
},
|
||||
"anthropic_api_url": {
|
||||
"display_name": "Anthropic API URL",
|
||||
"type": str,
|
||||
},
|
||||
"model_kwargs": {
|
||||
"display_name": "Model Kwargs",
|
||||
"field_type": "NestedDict",
|
||||
"advanced": True,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"field_type": "float",
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
anthropic_api_key: str,
|
||||
anthropic_api_url: str,
|
||||
model_kwargs: NestedDict = {},
|
||||
temperature: Optional[float] = None,
|
||||
) -> BaseLanguageModel:
|
||||
return Anthropic(
|
||||
anthropic_api_key=SecretStr(anthropic_api_key),
|
||||
anthropic_api_url=anthropic_api_url,
|
||||
model_kwargs=model_kwargs,
|
||||
temperature=temperature,
|
||||
)
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain_community.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ class QianfanChatEndpointComponent(CustomComponent):
|
|||
temperature: Optional[float] = None,
|
||||
penalty_score: Optional[float] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
) -> BaseLLM:
|
||||
) -> BaseLanguageModel:
|
||||
try:
|
||||
output = QianfanChatEndpoint( # type: ignore
|
||||
model=model,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.baidu_qianfan_endpoint import QianfanLLMEndpoint
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
|
||||
|
||||
class QianfanLLMEndpointComponent(CustomComponent):
|
||||
|
|
@ -78,7 +78,7 @@ class QianfanLLMEndpointComponent(CustomComponent):
|
|||
temperature: Optional[float] = None,
|
||||
penalty_score: Optional[float] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
) -> BaseLLM:
|
||||
) -> BaseLanguageModel:
|
||||
try:
|
||||
output = QianfanLLMEndpoint( # type: ignore
|
||||
model=model,
|
||||
|
|
|
|||
|
|
@ -1,67 +1,89 @@
|
|||
from typing import Callable, Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from langchain_community.chat_models.anthropic import ChatAnthropic
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from pydantic.v1.types import SecretStr
|
||||
|
||||
|
||||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
|
||||
|
||||
class ChatAnthropicComponent(CustomComponent):
|
||||
display_name = "ChatAnthropic"
|
||||
description = "`Anthropic` chat large language models."
|
||||
documentation = "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
|
||||
class AnthropicLLM(CustomComponent):
|
||||
display_name: str = "Anthropic"
|
||||
description: str = "Generate text using Anthropic Chat&Completion LLMs."
|
||||
icon = "Anthropic"
|
||||
|
||||
field_order = [
|
||||
"model",
|
||||
"anthropic_api_key",
|
||||
"max_tokens",
|
||||
"temperature",
|
||||
"anthropic_api_url",
|
||||
]
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Model Name",
|
||||
"options": [
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-sonnet-20240229",
|
||||
"claude-3-haiku-20240307",
|
||||
"claude-2.1",
|
||||
"claude-2.0",
|
||||
"claude-instant-1.2",
|
||||
"claude-instant-1",
|
||||
],
|
||||
"info": "Name of the model to use.",
|
||||
"required": True,
|
||||
"value": "claude-3-opus-20240229",
|
||||
},
|
||||
"anthropic_api_key": {
|
||||
"display_name": "Anthropic API Key",
|
||||
"field_type": "str",
|
||||
"required": True,
|
||||
"password": True,
|
||||
},
|
||||
"model_kwargs": {
|
||||
"display_name": "Model Kwargs",
|
||||
"field_type": "dict",
|
||||
"advanced": True,
|
||||
},
|
||||
"model_name": {
|
||||
"display_name": "Model Name",
|
||||
"field_type": "str",
|
||||
"advanced": False,
|
||||
"required": False,
|
||||
"options": ["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"],
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"field_type": "float",
|
||||
"info": "Your Anthropic API key.",
|
||||
},
|
||||
"max_tokens": {
|
||||
"display_name": "Max Tokens",
|
||||
"field_type": "int",
|
||||
"advanced": False,
|
||||
"required": False,
|
||||
"advanced": True,
|
||||
"value": 256,
|
||||
},
|
||||
"top_k": {"display_name": "Top K", "field_type": "int", "advanced": True},
|
||||
"top_p": {"display_name": "Top P", "field_type": "float", "advanced": True},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"field_type": "float",
|
||||
"value": 0.1,
|
||||
},
|
||||
"anthropic_api_url": {
|
||||
"display_name": "Anthropic API URL",
|
||||
"advanced": True,
|
||||
"info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
anthropic_api_key: str,
|
||||
model_kwargs: dict = {},
|
||||
model_name: str = "claude-3-opus-20240229",
|
||||
model: str,
|
||||
anthropic_api_key: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
temperature: Optional[float] = None,
|
||||
max_tokens: Optional[int] = 1024,
|
||||
top_k: Optional[int] = None,
|
||||
top_p: Optional[float] = None,
|
||||
) -> Union[BaseLanguageModel, Callable]:
|
||||
return ChatAnthropic(
|
||||
anthropic_api_key=SecretStr(anthropic_api_key),
|
||||
model_kwargs=model_kwargs,
|
||||
model_name=model_name,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens, # type: ignore
|
||||
top_k=top_k,
|
||||
top_p=top_p,
|
||||
)
|
||||
anthropic_api_url: Optional[str] = None,
|
||||
) -> BaseLanguageModel:
|
||||
# Set default API endpoint if not provided
|
||||
if not anthropic_api_url:
|
||||
anthropic_api_url = "https://api.anthropic.com"
|
||||
|
||||
try:
|
||||
output = ChatAnthropic(
|
||||
model_name=model,
|
||||
anthropic_api_key=(SecretStr(anthropic_api_key) if anthropic_api_key else None),
|
||||
max_tokens_to_sample=max_tokens, # type: ignore
|
||||
temperature=temperature,
|
||||
anthropic_api_url=anthropic_api_url,
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to Anthropic API.") from e
|
||||
|
||||
return output
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Callable, Dict, Optional, Union
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from langchain_community.chat_models.litellm import ChatLiteLLM, ChatLiteLLMException
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from langchain.llms import BaseLLM
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain_community.chat_models.openai import ChatOpenAI
|
||||
|
||||
from langflow.field_typing import BaseLanguageModel, NestedDict
|
||||
from langflow.field_typing import NestedDict
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ class ChatOpenAIComponent(CustomComponent):
|
|||
openai_api_base: Optional[str] = None,
|
||||
openai_api_key: Optional[str] = None,
|
||||
temperature: float = 0.7,
|
||||
) -> Union[BaseLanguageModel, BaseLLM]:
|
||||
) -> BaseLanguageModel:
|
||||
if not openai_api_base:
|
||||
openai_api_base = "https://api.openai.com/v1"
|
||||
return ChatOpenAI(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from typing import List, Optional, Union
|
||||
from typing import List, Optional
|
||||
|
||||
from langchain.llms import BaseLLM
|
||||
from langchain_community.chat_models.vertexai import ChatVertexAI
|
||||
from langchain_core.messages.base import BaseMessage
|
||||
|
||||
|
|
@ -74,7 +73,7 @@ class ChatVertexAIComponent(CustomComponent):
|
|||
top_k: int = 40,
|
||||
top_p: float = 0.95,
|
||||
verbose: bool = False,
|
||||
) -> Union[BaseLanguageModel, BaseLLM]:
|
||||
) -> BaseLanguageModel:
|
||||
return ChatVertexAI(
|
||||
credentials=credentials,
|
||||
examples=examples,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain.llms.huggingface_endpoint import HuggingFaceEndpoint
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
@ -32,7 +32,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
|
|||
task: str = "text2text-generation",
|
||||
huggingfacehub_api_token: Optional[str] = None,
|
||||
model_kwargs: Optional[dict] = None,
|
||||
) -> BaseLLM:
|
||||
) -> BaseLanguageModel:
|
||||
try:
|
||||
output = HuggingFaceEndpoint( # type: ignore
|
||||
endpoint_url=endpoint_url,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain_community.llms.ollama import Ollama
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
@ -118,7 +118,7 @@ class OllamaLLM(CustomComponent):
|
|||
tfs_z: Optional[float] = None,
|
||||
top_k: Optional[int] = None,
|
||||
top_p: Optional[int] = None,
|
||||
) -> BaseLLM:
|
||||
) -> BaseLanguageModel:
|
||||
if not base_url:
|
||||
base_url = "http://localhost:11434"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Callable, Dict, Optional, Union
|
||||
from typing import Dict, Optional
|
||||
|
||||
from langchain.llms import BaseLLM
|
||||
from langflow.field_typing import BaseLanguageModel
|
||||
from langchain_community.llms.vertexai import VertexAI
|
||||
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
|
@ -129,7 +129,7 @@ class VertexAIComponent(CustomComponent):
|
|||
top_p: float = 0.95,
|
||||
tuned_model_name: Optional[str] = None,
|
||||
verbose: bool = False,
|
||||
) -> Union[BaseLLM, Callable]:
|
||||
) -> BaseLanguageModel:
|
||||
return VertexAI(
|
||||
credentials=credentials,
|
||||
location=location,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
from .AmazonBedrockSpecs import AmazonBedrockComponent
|
||||
from .AnthropicLLMSpecs import AnthropicLLM
|
||||
from .AnthropicSpecs import AnthropicComponent
|
||||
from .AzureChatOpenAISpecs import AzureChatOpenAISpecsComponent
|
||||
from .BaiduQianfanChatEndpointsSpecs import QianfanChatEndpointComponent
|
||||
from .BaiduQianfanLLMEndpointsSpecs import QianfanLLMEndpointComponent
|
||||
from .ChatAnthropicSpecs import ChatAnthropicComponent
|
||||
from .ChatAnthropicSpecs import AnthropicLLM
|
||||
from .ChatLiteLLMSpecs import ChatLiteLLMComponent
|
||||
from .ChatOllamaEndpointSpecs import ChatOllamaComponent
|
||||
from .ChatOpenAISpecs import ChatOpenAIComponent
|
||||
|
|
@ -17,12 +14,11 @@ from .VertexAISpecs import VertexAIComponent
|
|||
|
||||
__all__ = [
|
||||
"AmazonBedrockComponent",
|
||||
"AnthropicLLM",
|
||||
"AnthropicComponent",
|
||||
"ChatAntropicSpecsComponent",
|
||||
"AzureChatOpenAISpecsComponent",
|
||||
"QianfanChatEndpointComponent",
|
||||
"QianfanLLMEndpointComponent",
|
||||
"ChatAnthropicComponent",
|
||||
"AnthropicLLM",
|
||||
"ChatLiteLLMComponent",
|
||||
"ChatOllamaComponent",
|
||||
"ChatOpenAIComponent",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from typing import Optional
|
|||
|
||||
from langchain.llms.base import BaseLanguageModel
|
||||
from langchain_openai import AzureChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
|
|
@ -105,13 +106,17 @@ class AzureChatOpenAIComponent(LCModelComponent):
|
|||
max_tokens: Optional[int] = 1000,
|
||||
stream: bool = False,
|
||||
) -> BaseLanguageModel:
|
||||
if api_key:
|
||||
secret_api_key = SecretStr(api_key)
|
||||
else:
|
||||
secret_api_key = None
|
||||
try:
|
||||
output = AzureChatOpenAI(
|
||||
model=model,
|
||||
azure_endpoint=azure_endpoint,
|
||||
azure_deployment=azure_deployment,
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
api_key=secret_api_key,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
|
|
@ -94,12 +95,17 @@ class OpenAIModelComponent(LCModelComponent):
|
|||
) -> Text:
|
||||
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
|
||||
|
||||
output = 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,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ class ChatOutput(ChatComponent):
|
|||
return_record: Optional[bool] = False,
|
||||
record_template: Optional[str] = "{text}",
|
||||
) -> Union[Text, Record]:
|
||||
return super().build(
|
||||
return super().build_with_record(
|
||||
sender=sender,
|
||||
sender_name=sender_name,
|
||||
input_value=input_value,
|
||||
session_id=session_id,
|
||||
return_record=return_record,
|
||||
record_template=record_template,
|
||||
record_template=record_template or "",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,5 +24,5 @@ class TextOutput(TextComponent):
|
|||
},
|
||||
}
|
||||
|
||||
def build(self, input_value: Optional[Text] = "", record_template: str = "") -> Text:
|
||||
def build(self, input_value: Optional[Text] = "", record_template: Optional[str] = "") -> Text:
|
||||
return super().build(input_value=input_value, record_template=record_template)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from typing import Callable, Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from langchain.retrievers import MultiQueryRetriever
|
||||
|
||||
from langflow.field_typing import BaseLLM, BaseRetriever, PromptTemplate
|
||||
from langflow.field_typing import BaseRetriever, PromptTemplate, BaseLanguageModel
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
|
||||
|
||||
|
|
@ -39,11 +39,11 @@ class MultiQueryRetrieverComponent(CustomComponent):
|
|||
|
||||
def build(
|
||||
self,
|
||||
llm: BaseLLM,
|
||||
llm: BaseLanguageModel,
|
||||
retriever: BaseRetriever,
|
||||
prompt: Optional[PromptTemplate] = None,
|
||||
parser_key: str = "lines",
|
||||
) -> Union[Callable, MultiQueryRetriever]:
|
||||
) -> MultiQueryRetriever:
|
||||
if not prompt:
|
||||
return MultiQueryRetriever.from_llm(llm=llm, retriever=retriever, parser_key=parser_key)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
from typing import Callable, Union
|
||||
|
||||
from langchain.agents.agent_toolkits.vectorstore.toolkit import VectorStoreInfo
|
||||
from langchain_community.vectorstores import VectorStore
|
||||
|
||||
|
|
@ -22,5 +20,5 @@ class VectorStoreInfoComponent(CustomComponent):
|
|||
vectorstore: VectorStore,
|
||||
description: str,
|
||||
name: str,
|
||||
) -> Union[VectorStoreInfo, Callable]:
|
||||
) -> VectorStoreInfo:
|
||||
return VectorStoreInfo(vectorstore=vectorstore, description=description, name=name)
|
||||
|
|
|
|||
68
src/backend/base/langflow/components/tools/PythonREPLTool.py
Normal file
68
src/backend/base/langflow/components/tools/PythonREPLTool.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import importlib
|
||||
|
||||
from langchain.agents import Tool
|
||||
from langchain_experimental.utilities import PythonREPL
|
||||
|
||||
from langflow.base.tools.base import build_status_from_tool
|
||||
from langflow.custom import CustomComponent
|
||||
|
||||
|
||||
class PythonREPLToolComponent(CustomComponent):
|
||||
display_name = "Python REPL Tool"
|
||||
description = "A tool for running Python code in a REPL environment."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"name": {"display_name": "Name", "info": "The name of the tool."},
|
||||
"description": {"display_name": "Description", "info": "A description of the tool."},
|
||||
"global_imports": {
|
||||
"display_name": "Global Imports",
|
||||
"info": "A list of modules to import globally, e.g. ['math', 'numpy'].",
|
||||
},
|
||||
}
|
||||
|
||||
def get_globals(self, globals: list[str]) -> dict:
|
||||
"""
|
||||
Retrieves the global variables from the specified modules.
|
||||
|
||||
Args:
|
||||
globals (list[str]): A list of module names.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the global variables from the specified modules.
|
||||
"""
|
||||
global_dict = {}
|
||||
for module in globals:
|
||||
try:
|
||||
imported_module = importlib.import_module(module)
|
||||
global_dict[imported_module.__name__] = imported_module
|
||||
except ImportError:
|
||||
print(f"Could not import module {module}")
|
||||
return global_dict
|
||||
|
||||
def build(
|
||||
self,
|
||||
name: str = "python_repl",
|
||||
description: str = "A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`.",
|
||||
global_imports: list[str] = ["math"],
|
||||
) -> Tool:
|
||||
"""
|
||||
Builds a Python REPL tool.
|
||||
|
||||
Args:
|
||||
name (str, optional): The name of the tool. Defaults to "python_repl".
|
||||
description (str, optional): The description of the tool. Defaults to "A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`. ".
|
||||
global_imports (list[str], optional): A list of global imports to be available in the Python REPL. Defaults to ["math"].
|
||||
|
||||
Returns:
|
||||
Tool: The built Python REPL tool.
|
||||
"""
|
||||
_globals = self.get_globals(global_imports)
|
||||
python_repl = PythonREPL(_globals=_globals)
|
||||
tool = Tool(
|
||||
name=name,
|
||||
description=description,
|
||||
func=python_repl.run,
|
||||
)
|
||||
self.status = build_status_from_tool(tool)
|
||||
return tool
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
from .PythonREPLTool import PythonREPLToolComponent
|
||||
from .RetrieverTool import RetrieverToolComponent
|
||||
from .SearchAPITool import SearchApiToolComponent
|
||||
from .SearchApi import SearchApi
|
||||
from .SearchAPITool import SearchApiToolComponent
|
||||
|
||||
__all__ = ["RetrieverToolComponent", "SearchApiToolComponent", "SearchApi"]
|
||||
|
||||
__all__ = ["RetrieverToolComponent", "SearchApiToolComponent", "SearchApi", "PythonREPLToolComponent"]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from typing import List, Optional
|
|||
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
|
||||
from langflow.components.vectorstores.Pinecone import PineconeComponent
|
||||
from langflow.field_typing import Embeddings, Text
|
||||
from langflow.field_typing.constants import NestedDict
|
||||
from langflow.schema import Record
|
||||
|
||||
|
||||
|
|
@ -32,7 +33,6 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
"default": "",
|
||||
"required": True,
|
||||
},
|
||||
"search_kwargs": {"display_name": "Search Kwargs", "default": "{}"},
|
||||
"pool_threads": {
|
||||
"display_name": "Pool Threads",
|
||||
"default": 1,
|
||||
|
|
@ -57,6 +57,7 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
pinecone_api_key: Optional[str] = None,
|
||||
namespace: Optional[str] = "default",
|
||||
search_type: str = "similarity",
|
||||
search_kwargs: Optional[NestedDict] = None,
|
||||
) -> List[Record]: # type: ignore[override]
|
||||
vector_store = super().build(
|
||||
embedding=embedding,
|
||||
|
|
@ -70,7 +71,13 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
|
|||
)
|
||||
if not vector_store:
|
||||
raise ValueError("Failed to load the Pinecone index.")
|
||||
if search_kwargs is None:
|
||||
search_kwargs = {}
|
||||
|
||||
return self.search_with_vector_store(
|
||||
vector_store=vector_store, input_value=input_value, search_type=search_type, k=number_of_results
|
||||
vector_store=vector_store,
|
||||
input_value=input_value,
|
||||
search_type=search_type,
|
||||
k=number_of_results,
|
||||
**search_kwargs,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -86,13 +86,18 @@ class QdrantSearchComponent(QdrantComponent, LCVectorStoreComponent):
|
|||
port=port,
|
||||
prefer_grpc=prefer_grpc,
|
||||
prefix=prefix,
|
||||
search_kwargs=search_kwargs,
|
||||
timeout=timeout,
|
||||
url=url,
|
||||
)
|
||||
if not vector_store:
|
||||
raise ValueError("Failed to load the Qdrant index.")
|
||||
if search_kwargs is None:
|
||||
search_kwargs = {}
|
||||
|
||||
return self.search_with_vector_store(
|
||||
vector_store=vector_store, input_value=input_value, search_type=search_type, k=number_of_results
|
||||
vector_store=vector_store,
|
||||
input_value=input_value,
|
||||
search_type=search_type,
|
||||
k=number_of_results,
|
||||
**search_kwargs,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ class AstraDBVectorStoreComponent(CustomComponent):
|
|||
bulk_insert_batch_concurrency: Optional[int] = None,
|
||||
bulk_insert_overwrite_concurrency: Optional[int] = None,
|
||||
bulk_delete_concurrency: Optional[int] = None,
|
||||
setup_mode: str = "Async",
|
||||
setup_mode: str = "Sync",
|
||||
pre_delete_collection: bool = False,
|
||||
metadata_indexing_include: Optional[List[str]] = None,
|
||||
metadata_indexing_exclude: Optional[List[str]] = None,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from langchain_community.vectorstores.mongodb_atlas import MongoDBAtlasVectorSearch
|
||||
from langflow.field_typing import Embeddings, NestedDict
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
|
@ -19,7 +19,6 @@ class MongoDBAtlasComponent(CustomComponent):
|
|||
"db_name": {"display_name": "Database Name"},
|
||||
"index_name": {"display_name": "Index Name"},
|
||||
"mongodb_atlas_cluster_uri": {"display_name": "MongoDB Atlas Cluster URI"},
|
||||
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
|
||||
}
|
||||
|
||||
def build(
|
||||
|
|
@ -30,9 +29,7 @@ class MongoDBAtlasComponent(CustomComponent):
|
|||
db_name: str = "",
|
||||
index_name: str = "",
|
||||
mongodb_atlas_cluster_uri: str = "",
|
||||
search_kwargs: Optional[NestedDict] = None,
|
||||
) -> MongoDBAtlasVectorSearch:
|
||||
search_kwargs = search_kwargs or {}
|
||||
try:
|
||||
from pymongo import MongoClient
|
||||
except ImportError:
|
||||
|
|
@ -56,7 +53,6 @@ class MongoDBAtlasComponent(CustomComponent):
|
|||
db_name=db_name,
|
||||
index_name=index_name,
|
||||
mongodb_atlas_cluster_uri=mongodb_atlas_cluster_uri,
|
||||
search_kwargs=search_kwargs,
|
||||
)
|
||||
else:
|
||||
vector_store = MongoDBAtlasVectorSearch(
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ class PineconeComponent(CustomComponent):
|
|||
"default": "",
|
||||
"required": True,
|
||||
},
|
||||
"search_kwargs": {"display_name": "Search Kwargs", "default": "{}"},
|
||||
"pool_threads": {
|
||||
"display_name": "Pool Threads",
|
||||
"default": 1,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain.schema import BaseRetriever
|
|||
from langchain_community.vectorstores import VectorStore
|
||||
from langchain_community.vectorstores.qdrant import Qdrant
|
||||
|
||||
from langflow.field_typing import Embeddings, NestedDict
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
|
@ -37,7 +37,6 @@ class QdrantComponent(CustomComponent):
|
|||
"port": {"display_name": "Port", "advanced": True},
|
||||
"prefer_grpc": {"display_name": "Prefer gRPC", "advanced": True},
|
||||
"prefix": {"display_name": "Prefix", "advanced": True},
|
||||
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
|
||||
"timeout": {"display_name": "Timeout", "advanced": True},
|
||||
"url": {"display_name": "URL", "advanced": True},
|
||||
}
|
||||
|
|
@ -59,7 +58,6 @@ class QdrantComponent(CustomComponent):
|
|||
port: Optional[int] = 6333,
|
||||
prefer_grpc: bool = False,
|
||||
prefix: Optional[str] = None,
|
||||
search_kwargs: Optional[NestedDict] = None,
|
||||
timeout: Optional[int] = None,
|
||||
url: Optional[str] = None,
|
||||
) -> Union[VectorStore, Qdrant, BaseRetriever]:
|
||||
|
|
@ -111,7 +109,6 @@ class QdrantComponent(CustomComponent):
|
|||
port=port,
|
||||
prefer_grpc=prefer_grpc,
|
||||
prefix=prefix,
|
||||
search_kwargs=search_kwargs,
|
||||
timeout=timeout,
|
||||
url=url,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from langchain_community.vectorstores import VectorStore
|
|||
from langchain_community.vectorstores.supabase import SupabaseVectorStore
|
||||
from supabase.client import Client, create_client
|
||||
|
||||
from langflow.field_typing import Embeddings, NestedDict
|
||||
from langflow.field_typing import Embeddings
|
||||
from langflow.interface.custom.custom_component import CustomComponent
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
|
|
@ -19,7 +19,6 @@ class SupabaseComponent(CustomComponent):
|
|||
"inputs": {"display_name": "Input", "input_types": ["Document", "Record"]},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"query_name": {"display_name": "Query Name"},
|
||||
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
|
||||
"supabase_service_key": {"display_name": "Supabase Service Key"},
|
||||
"supabase_url": {"display_name": "Supabase URL"},
|
||||
"table_name": {"display_name": "Table Name", "advanced": True},
|
||||
|
|
@ -30,7 +29,6 @@ class SupabaseComponent(CustomComponent):
|
|||
embedding: Embeddings,
|
||||
inputs: Optional[List[Record]] = None,
|
||||
query_name: str = "",
|
||||
search_kwargs: NestedDict = {},
|
||||
supabase_service_key: str = "",
|
||||
supabase_url: str = "",
|
||||
table_name: str = "",
|
||||
|
|
@ -46,7 +44,6 @@ class SupabaseComponent(CustomComponent):
|
|||
documents=documents,
|
||||
embedding=embedding,
|
||||
query_name=query_name,
|
||||
search_kwargs=search_kwargs,
|
||||
client=supabase,
|
||||
table_name=table_name,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -249,7 +249,10 @@ class Graph:
|
|||
vertex.update_raw_params({"session_id": session_id})
|
||||
# Process the graph
|
||||
try:
|
||||
await self.process()
|
||||
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)
|
||||
self.increment_run_count()
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
|
|
@ -293,18 +296,27 @@ class Graph:
|
|||
# run the async function in a sync way
|
||||
# this could be used in a FastAPI endpoint
|
||||
# so we should take care of the event loop
|
||||
loop = asyncio.get_event_loop()
|
||||
return loop.run_until_complete(
|
||||
self.arun(
|
||||
inputs=inputs,
|
||||
inputs_components=input_components,
|
||||
types=types,
|
||||
outputs=outputs,
|
||||
session_id=session_id,
|
||||
stream=stream,
|
||||
)
|
||||
coro = self.arun(
|
||||
inputs=inputs,
|
||||
inputs_components=input_components,
|
||||
types=types,
|
||||
outputs=outputs,
|
||||
session_id=session_id,
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
try:
|
||||
# Attempt to get the running event loop; if none, an exception is raised
|
||||
loop = asyncio.get_running_loop()
|
||||
if loop.is_closed():
|
||||
raise RuntimeError("The running event loop is closed.")
|
||||
except RuntimeError:
|
||||
# If there's no running event loop or it's closed, use asyncio.run
|
||||
return asyncio.run(coro)
|
||||
|
||||
# If there's an existing, open event loop, use it to run the async function
|
||||
return loop.run_until_complete(coro)
|
||||
|
||||
async def arun(
|
||||
self,
|
||||
inputs: list[Dict[str, str]],
|
||||
|
|
@ -345,7 +357,7 @@ class Graph:
|
|||
if types is None:
|
||||
types = []
|
||||
for _ in range(len(inputs) - len(types)):
|
||||
types.append("any")
|
||||
types.append("chat") # default to chat
|
||||
for run_inputs, components, input_type in zip(inputs, inputs_components, types):
|
||||
run_outputs = await self._run(
|
||||
inputs=run_inputs,
|
||||
|
|
@ -733,8 +745,10 @@ class Graph:
|
|||
vertices.append(vertex)
|
||||
return vertices
|
||||
|
||||
async def process(self) -> "Graph":
|
||||
async def process(self, start_component_id: Optional[str] = None) -> "Graph":
|
||||
"""Processes the graph with vertices in each layer run in parallel."""
|
||||
|
||||
self.sort_vertices(start_component_id=start_component_id)
|
||||
vertices_layers = self.sorted_vertices_layers
|
||||
vertex_task_run_count: Dict[str, int] = {}
|
||||
for layer_index, layer in enumerate(vertices_layers):
|
||||
|
|
@ -904,7 +918,7 @@ class Graph:
|
|||
return ChatVertex
|
||||
elif node_name in ["ShouldRunNext"]:
|
||||
return RoutingVertex
|
||||
elif node_name in ["SharedState", "Notify", "GetNotified"]:
|
||||
elif node_name in ["SharedState", "Notify", "Listen"]:
|
||||
return StateVertex
|
||||
elif node_base_type in lazy_load_vertex_dict.VERTEX_TYPE_MAP:
|
||||
return lazy_load_vertex_dict.VERTEX_TYPE_MAP[node_base_type]
|
||||
|
|
@ -1116,6 +1130,34 @@ class Graph:
|
|||
|
||||
return vertices_layers
|
||||
|
||||
def sort_layer_by_dependency(self, vertices_layers: List[List[str]]) -> List[List[str]]:
|
||||
"""Sorts the vertices in each layer by dependency, ensuring no vertex depends on a subsequent vertex."""
|
||||
sorted_layers = []
|
||||
|
||||
for layer in vertices_layers:
|
||||
sorted_layer = self._sort_single_layer_by_dependency(layer)
|
||||
sorted_layers.append(sorted_layer)
|
||||
|
||||
return sorted_layers
|
||||
|
||||
def _sort_single_layer_by_dependency(self, layer: List[str]) -> List[str]:
|
||||
"""Sorts a single layer by dependency using a stable sorting method."""
|
||||
# Build a map of each vertex to its index in the layer for quick lookup.
|
||||
index_map = {vertex: index for index, vertex in enumerate(layer)}
|
||||
# Create a sorted copy of the layer based on dependency order.
|
||||
sorted_layer = sorted(layer, key=lambda vertex: self._max_dependency_index(vertex, index_map), reverse=True)
|
||||
|
||||
return sorted_layer
|
||||
|
||||
def _max_dependency_index(self, vertex_id: str, index_map: Dict[str, int]) -> int:
|
||||
"""Finds the highest index a given vertex's dependencies occupy in the same layer."""
|
||||
vertex = self.get_vertex(vertex_id)
|
||||
max_index = -1
|
||||
for successor in vertex.successors: # Assuming vertex.successors is a list of successor vertex identifiers.
|
||||
if successor.id in index_map:
|
||||
max_index = max(max_index, index_map[successor.id])
|
||||
return max_index
|
||||
|
||||
def sort_vertices(
|
||||
self,
|
||||
stop_component_id: Optional[str] = None,
|
||||
|
|
@ -1137,6 +1179,9 @@ class Graph:
|
|||
vertices_layers = self.layered_topological_sort(vertices)
|
||||
vertices_layers = self.sort_by_avg_build_time(vertices_layers)
|
||||
# vertices_layers = self.sort_chat_inputs_first(vertices_layers)
|
||||
# Now we should sort each layer in a way that we make sure
|
||||
# vertex V does not depend on vertex V+1
|
||||
vertices_layers = self.sort_layer_by_dependency(vertices_layers)
|
||||
self.increment_run_count()
|
||||
self._sorted_vertices_layers = vertices_layers
|
||||
first_layer = vertices_layers[0]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
from typing import TYPE_CHECKING, Callable
|
||||
|
||||
from langflow.services.deps import get_state_service
|
||||
from loguru import logger
|
||||
|
||||
from langflow.services.deps import get_settings_service, get_state_service
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.state.service import StateService
|
||||
|
||||
|
||||
class GraphStateManager:
|
||||
def __init__(self):
|
||||
self.state_service: "StateService" = get_state_service()
|
||||
try:
|
||||
self.state_service: "StateService" = get_state_service()
|
||||
except Exception as e:
|
||||
logger.debug(f"Error getting state service. Defaulting to InMemoryStateService: {e}")
|
||||
from langflow.services.state.service import InMemoryStateService
|
||||
|
||||
self.state_service = InMemoryStateService(get_settings_service())
|
||||
|
||||
def append_state(self, key, new_state, run_id: str):
|
||||
self.state_service.append_state(key, new_state, run_id)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import ast
|
||||
import asyncio
|
||||
import inspect
|
||||
import os
|
||||
import types
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Optional
|
||||
import os
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
|
@ -315,7 +315,8 @@ class Vertex:
|
|||
raise e
|
||||
params[field_name] = full_path
|
||||
elif field.get("required"):
|
||||
raise ValueError(f"File path not found for {self.display_name}")
|
||||
field_display_name = field.get("display_name")
|
||||
raise ValueError(f"File path not found for {field_display_name} in component {self.display_name}")
|
||||
elif field.get("type") in DIRECT_TYPES and params.get(field_name) is None:
|
||||
val = field.get("value")
|
||||
if field.get("type") == "code":
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from typing import TYPE_CHECKING, Any, Callable, Coroutine, List, Optional, Tuple, Type, Union, cast
|
||||
|
||||
from pydantic.v1 import BaseModel, Field, create_model
|
||||
from sqlmodel import select
|
||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, List, Optional, Tuple, Type, Union, cast
|
||||
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME, Record
|
||||
from langflow.services.database.models.flow.model import Flow
|
||||
from langflow.services.deps import session_scope
|
||||
from pydantic.v1 import BaseModel, Field, create_model
|
||||
from sqlmodel import select
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.graph.base import Graph
|
||||
|
|
@ -86,7 +85,7 @@ async def run_flow(
|
|||
return await graph.arun(inputs_list, inputs_components=inputs_components, types=types)
|
||||
|
||||
|
||||
def generate_function_for_flow(inputs: List["Vertex"], flow_id: str) -> Coroutine:
|
||||
def generate_function_for_flow(inputs: List["Vertex"], flow_id: str) -> Callable[..., Awaitable[Any]]:
|
||||
"""
|
||||
Generate a dynamic flow function based on the given inputs and flow ID.
|
||||
|
||||
|
|
@ -145,7 +144,9 @@ async def flow_function({func_args}):
|
|||
return local_scope["flow_function"]
|
||||
|
||||
|
||||
def build_function_and_schema(flow_record: Record, graph: "Graph") -> Tuple[Callable, Type[BaseModel]]:
|
||||
def build_function_and_schema(
|
||||
flow_record: Record, graph: "Graph"
|
||||
) -> Tuple[Callable[..., Awaitable[Any]], Type[BaseModel]]:
|
||||
"""
|
||||
Builds a dynamic function and schema for a given flow.
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ def log_node_changes(node_changes_log):
|
|||
logger.debug("\n".join(formatted_messages))
|
||||
|
||||
|
||||
def load_starter_projects():
|
||||
def load_starter_projects() -> list[tuple[Path, dict]]:
|
||||
starter_projects = []
|
||||
folder = Path(__file__).parent / "starter_projects"
|
||||
for file in folder.glob("*.json"):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -45,7 +45,9 @@
|
|||
"name": "template",
|
||||
"display_name": "Template",
|
||||
"advanced": false,
|
||||
"input_types": ["Text"],
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -136,14 +138,24 @@
|
|||
"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,
|
||||
|
|
@ -210,7 +222,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"],
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"value": [
|
||||
"https://www.promptingguide.ai/techniques/prompt_chaining"
|
||||
]
|
||||
|
|
@ -219,13 +233,17 @@
|
|||
},
|
||||
"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": [],
|
||||
|
|
@ -260,7 +278,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 ) -> Union[Text, Record]:\n return super().build(\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,\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 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 ) -> 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,
|
||||
|
|
@ -284,7 +302,9 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": ["Text"],
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -308,7 +328,9 @@
|
|||
"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,7 +362,10 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": ["Machine", "User"],
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -348,7 +373,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -368,7 +395,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -387,13 +416,20 @@
|
|||
"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.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": ["Text", "Record", "object", "str"],
|
||||
"base_classes": [
|
||||
"Text",
|
||||
"Record",
|
||||
"object",
|
||||
"str"
|
||||
],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -404,7 +440,10 @@
|
|||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": ["Text", "Record"],
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -444,7 +483,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -453,7 +494,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\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-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 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=openai_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.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-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",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -529,7 +570,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -548,7 +591,9 @@
|
|||
"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",
|
||||
|
|
@ -567,7 +612,9 @@
|
|||
"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": {
|
||||
|
|
@ -606,7 +653,9 @@
|
|||
"info": "System message to pass to the model.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -637,7 +686,11 @@
|
|||
},
|
||||
"description": "Generates text using OpenAI LLMs.",
|
||||
"icon": "OpenAI",
|
||||
"base_classes": ["str", "Text", "object"],
|
||||
"base_classes": [
|
||||
"str",
|
||||
"Text",
|
||||
"object"
|
||||
],
|
||||
"display_name": "OpenAI",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -651,7 +704,9 @@
|
|||
"stream": null,
|
||||
"system_message": null
|
||||
},
|
||||
"output_types": ["Text"],
|
||||
"output_types": [
|
||||
"Text"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [
|
||||
|
|
@ -724,20 +779,28 @@
|
|||
"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": [],
|
||||
|
|
@ -797,7 +860,10 @@
|
|||
"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,
|
||||
|
|
@ -821,20 +887,28 @@
|
|||
"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.",
|
||||
"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": [],
|
||||
|
|
@ -863,11 +937,18 @@
|
|||
"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"
|
||||
}
|
||||
|
|
@ -887,11 +968,17 @@
|
|||
"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"
|
||||
}
|
||||
|
|
@ -911,11 +998,18 @@
|
|||
"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"
|
||||
}
|
||||
|
|
@ -935,11 +1029,20 @@
|
|||
"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"
|
||||
}
|
||||
|
|
@ -959,11 +1062,17 @@
|
|||
"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"
|
||||
}
|
||||
|
|
@ -986,4 +1095,4 @@
|
|||
"name": "Blog Writer",
|
||||
"last_tested_version": "1.0.0a0",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -256,7 +256,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 ) -> Union[Text, Record]:\n return super().build(\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,\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 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 ) -> 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,
|
||||
|
|
@ -452,7 +452,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 ) -> Union[Text, Record]:\n return super().build(\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,\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 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 ) -> 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,
|
||||
|
|
@ -647,7 +647,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 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",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -884,7 +884,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\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-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 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=openai_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.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-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",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -1270,7 +1270,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\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-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 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=openai_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.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-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",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,14 +1,14 @@
|
|||
from typing import ClassVar, Dict, List, Optional
|
||||
|
||||
from langchain.agents import types
|
||||
from loguru import logger
|
||||
|
||||
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.legacy_custom.customs import get_custom_nodes
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.agents import AgentFrontendNode
|
||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||
from langflow.utils.util import build_template_from_method
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class AgentCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ from typing import Any, ClassVar, Dict, List, Optional, Type
|
|||
|
||||
from langchain import chains
|
||||
from langchain_experimental.sql import SQLDatabaseChain
|
||||
from loguru import logger
|
||||
|
||||
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.legacy_custom.customs import get_custom_nodes
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.chains import ChainFrontendNode
|
||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||
from langflow.utils.util import build_template_from_method
|
||||
from loguru import logger
|
||||
|
||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ from loguru import logger
|
|||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class DocumentLoaderCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ from loguru import logger
|
|||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import embedding_type_to_cls_dict
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class EmbeddingCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ from loguru import logger
|
|||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import llm_type_to_cls_dict
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.llms import LLMFrontendNode
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class LLMCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
from typing import ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import memory_type_to_cls_dict
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.legacy_custom.customs import get_custom_nodes
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.frontend_node.memories import MemoryFrontendNode
|
||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||
from langflow.utils.util import build_template_from_method
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class MemoryCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from typing import ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langchain import output_parsers
|
||||
from loguru import logger
|
||||
|
||||
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_class, build_template_from_method
|
||||
from langflow.utils.util import build_template_from_method
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class OutputParserCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ from typing import Dict, List, Optional, Type
|
|||
|
||||
from langchain import prompts
|
||||
from loguru import logger
|
||||
|
||||
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.legacy_custom.customs import get_custom_nodes
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.prompts import PromptFrontendNode
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class PromptCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from typing import Any, ClassVar, Dict, List, Optional, Type
|
||||
|
||||
from langchain_community import retrievers
|
||||
from loguru import logger
|
||||
|
||||
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.retrievers import RetrieverFrontendNode
|
||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||
from langflow.utils.util import build_template_from_method
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class RetrieverCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -5,17 +5,6 @@ from loguru import logger
|
|||
from langflow.graph import Graph
|
||||
|
||||
|
||||
async def build_sorted_vertices(data_graph, flow_id: str) -> Tuple[Graph, Dict]:
|
||||
"""
|
||||
Build langchain object from data_graph.
|
||||
"""
|
||||
|
||||
logger.debug("Building langchain object")
|
||||
graph = Graph.from_payload(data_graph, flow_id=flow_id)
|
||||
|
||||
return graph, {}
|
||||
|
||||
|
||||
def get_memory_key(langchain_object):
|
||||
"""
|
||||
Given a LangChain object, this function retrieves the current memory key from the object's memory attribute.
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ from loguru import logger
|
|||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class TextSplitterCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ from typing import Callable, Dict, List, Optional
|
|||
|
||||
from langchain.agents import agent_toolkits
|
||||
from loguru import logger
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.importing.utils import import_class, import_module
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class ToolkitCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from langflow.template.field.base import TemplateField
|
|||
from langflow.template.template.base import Template
|
||||
from langflow.utils import util
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.util import build_template_from_class
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
|
||||
TOOL_INPUTS = {
|
||||
"str": TemplateField(
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ from typing import Dict, List, Optional, Type
|
|||
|
||||
from langchain_community import utilities
|
||||
from loguru import logger
|
||||
|
||||
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.legacy_custom.customs import get_custom_nodes
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
class UtilityCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ import json
|
|||
import os
|
||||
import re
|
||||
from io import BytesIO
|
||||
from typing import Dict
|
||||
|
||||
import yaml
|
||||
from docstring_parser import parse
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from loguru import logger
|
||||
from PIL.Image import Image
|
||||
|
||||
from langflow.services.chat.config import ChatConfig
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.utils.util import format_dict, get_base_classes, get_default_factory
|
||||
from loguru import logger
|
||||
from PIL.Image import Image
|
||||
|
||||
|
||||
def load_file_into_dict(file_path: str) -> dict:
|
||||
|
|
@ -100,7 +102,6 @@ def setup_llm_caching():
|
|||
|
||||
def set_langchain_cache(settings):
|
||||
from langchain.globals import set_llm_cache
|
||||
|
||||
from langflow.interface.importing.utils import import_class
|
||||
|
||||
if cache_type := os.getenv("LANGFLOW_LANGCHAIN_CACHE"):
|
||||
|
|
@ -114,3 +115,51 @@ def set_langchain_cache(settings):
|
|||
logger.warning(f"Could not import {cache_type}. ")
|
||||
else:
|
||||
logger.info("No LLM cache set.")
|
||||
|
||||
|
||||
def build_template_from_class(name: str, type_to_cls_dict: Dict, add_function: bool = False):
|
||||
classes = [item.__name__ for item in type_to_cls_dict.values()]
|
||||
|
||||
# Raise error if name is not in chains
|
||||
if name not in classes:
|
||||
raise ValueError(f"{name} not found.")
|
||||
|
||||
for _type, v in type_to_cls_dict.items():
|
||||
if v.__name__ == name:
|
||||
_class = v
|
||||
|
||||
# Get the docstring
|
||||
docs = parse(_class.__doc__)
|
||||
|
||||
variables = {"_type": _type}
|
||||
|
||||
if "__fields__" in _class.__dict__:
|
||||
for class_field_items, value in _class.__fields__.items():
|
||||
if class_field_items in ["callback_manager"]:
|
||||
continue
|
||||
variables[class_field_items] = {}
|
||||
for name_, value_ in value.__repr_args__():
|
||||
if name_ == "default_factory":
|
||||
try:
|
||||
variables[class_field_items]["default"] = get_default_factory(
|
||||
module=_class.__base__.__module__,
|
||||
function=value_,
|
||||
)
|
||||
except Exception:
|
||||
variables[class_field_items]["default"] = None
|
||||
elif name_ not in ["name"]:
|
||||
variables[class_field_items][name_] = value_
|
||||
|
||||
variables[class_field_items]["placeholder"] = (
|
||||
docs.params[class_field_items] if class_field_items in docs.params else ""
|
||||
)
|
||||
base_classes = get_base_classes(_class)
|
||||
# Adding function to base classes to allow
|
||||
# the output to be a function
|
||||
if add_function:
|
||||
base_classes.append("Callable")
|
||||
return {
|
||||
"template": format_dict(variables, name),
|
||||
"description": docs.short_description or "",
|
||||
"base_classes": base_classes,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
from typing import Dict, List, Optional
|
||||
|
||||
from langchain_community.utilities import requests
|
||||
from loguru import logger
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.utils.util import build_template_from_class
|
||||
from langflow.interface.utils import build_template_from_class
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class WrapperCreator(LangChainTypeCreator):
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from loguru import logger
|
||||
from rich import print as rprint
|
||||
|
||||
from langflow.api import router
|
||||
from langflow.initial_setup.setup import create_or_update_starter_projects
|
||||
|
|
@ -28,6 +29,8 @@ def get_lifespan(fix_migration=False, socketio_server=None):
|
|||
LangfuseInstance.update()
|
||||
create_or_update_starter_projects()
|
||||
yield
|
||||
# Shutdown message
|
||||
rprint("[bold red]Shutting down Langflow...[/bold red]")
|
||||
teardown_services()
|
||||
|
||||
return lifespan
|
||||
|
|
|
|||
|
|
@ -2,21 +2,55 @@ import json
|
|||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from langflow.graph import Graph
|
||||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.processing.process import process_tweaks, run_graph
|
||||
from langflow.utils.logger import configure
|
||||
from langflow.utils.util import update_settings
|
||||
|
||||
|
||||
def load_flow_from_json(flow: Union[Path, str, dict], tweaks: Optional[dict] = None) -> Graph:
|
||||
def load_flow_from_json(
|
||||
flow: Union[Path, str, dict],
|
||||
tweaks: Optional[dict] = None,
|
||||
log_level: Optional[str] = None,
|
||||
log_file: Optional[str] = None,
|
||||
env_file: Optional[str] = None,
|
||||
cache: Optional[str] = None,
|
||||
disable_logs: Optional[bool] = True,
|
||||
) -> Graph:
|
||||
"""
|
||||
Load flow from a JSON file or a JSON object.
|
||||
Load a flow graph from a JSON file or a JSON object.
|
||||
|
||||
Args:
|
||||
flow (Union[Path, str, dict]): The flow to load. It can be a file path (str or Path object)
|
||||
or a JSON object (dict).
|
||||
tweaks (Optional[dict]): Optional tweaks to apply to the loaded flow graph.
|
||||
log_level (Optional[str]): Optional log level to configure for the flow processing.
|
||||
log_file (Optional[str]): Optional log file to configure for the flow processing.
|
||||
env_file (Optional[str]): Optional .env file to override environment variables.
|
||||
cache (Optional[str]): Optional cache path to update the flow settings.
|
||||
disable_logs (Optional[bool], default=True): Optional flag to disable logs during flow processing.
|
||||
If log_level or log_file are set, disable_logs is not used.
|
||||
|
||||
Returns:
|
||||
Graph: The loaded flow graph as a Graph object.
|
||||
|
||||
Raises:
|
||||
TypeError: If the input is neither a file path (str or Path object) nor a JSON object (dict).
|
||||
|
||||
:param flow: JSON file path or JSON object
|
||||
:param tweaks: Optional tweaks to be processed
|
||||
:param build: If True, build the graph, otherwise return the graph object
|
||||
:return: Langchain object or Graph object depending on the build parameter
|
||||
"""
|
||||
# If input is a file path, load JSON from the file
|
||||
log_file_path = Path(log_file) if log_file else None
|
||||
configure(log_level=log_level, log_file=log_file_path, disable=disable_logs)
|
||||
|
||||
# override env variables with .env file
|
||||
if env_file:
|
||||
load_dotenv(env_file, override=True)
|
||||
|
||||
# Update settings with cache and components path
|
||||
update_settings(cache=cache)
|
||||
|
||||
if isinstance(flow, (str, Path)):
|
||||
with open(flow, "r", encoding="utf-8") as f:
|
||||
flow_graph = json.load(f)
|
||||
|
|
@ -41,22 +75,44 @@ def run_flow_from_json(
|
|||
input_type: str = "chat",
|
||||
output_type: str = "chat",
|
||||
output_component: Optional[str] = None,
|
||||
log_level: Optional[str] = None,
|
||||
log_file: Optional[str] = None,
|
||||
env_file: Optional[str] = None,
|
||||
cache: Optional[str] = None,
|
||||
disable_logs: Optional[bool] = True,
|
||||
) -> List[RunOutputs]:
|
||||
"""
|
||||
Runs a JSON flow by loading it from a file or dictionary and executing it with the given input value.
|
||||
Run a flow from a JSON file or dictionary.
|
||||
|
||||
Args:
|
||||
flow (Union[Path, str, dict]): The path to the JSON file, or the JSON dictionary representing the flow.
|
||||
flow (Union[Path, str, dict]): The path to the JSON file or the JSON dictionary representing the flow.
|
||||
input_value (str): The input value to be processed by the flow.
|
||||
tweaks (Optional[dict], optional): Optional tweaks to be applied to the flow. Defaults to None.
|
||||
input_type (str, optional): The type of the input value. Defaults to "chat".
|
||||
output_type (str, optional): The type of the output value. Defaults to "chat".
|
||||
output_component (Optional[str], optional): The specific output component to retrieve. Defaults to None.
|
||||
output_component (Optional[str], optional): The specific component to output. Defaults to None.
|
||||
log_level (Optional[str], optional): The log level to use. Defaults to None.
|
||||
log_file (Optional[str], optional): The log file to write logs to. Defaults to None.
|
||||
env_file (Optional[str], optional): The environment file to load. Defaults to None.
|
||||
cache (Optional[str], optional): The cache directory to use. Defaults to None.
|
||||
disable_logs (Optional[bool], optional): Whether to disable logs. Defaults to True.
|
||||
|
||||
Returns:
|
||||
None: The result of running the flow.
|
||||
List[RunOutputs]: A list of RunOutputs objects representing the results of running the flow.
|
||||
"""
|
||||
graph = load_flow_from_json(flow, tweaks)
|
||||
# Set all streaming to false
|
||||
if tweaks is None:
|
||||
tweaks = {}
|
||||
tweaks["stream"] = False
|
||||
graph = load_flow_from_json(
|
||||
flow=flow,
|
||||
tweaks=tweaks,
|
||||
log_level=log_level,
|
||||
log_file=log_file,
|
||||
env_file=env_file,
|
||||
cache=cache,
|
||||
disable_logs=disable_logs,
|
||||
)
|
||||
result = run_graph(
|
||||
graph=graph,
|
||||
input_value=input_value,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ from langflow.graph.graph.base import Graph
|
|||
from langflow.graph.schema import RunOutputs
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.interface.run import get_memory_key, update_memory_keys
|
||||
from langflow.schema.graph import InputValue, Tweaks
|
||||
from langflow.schema.schema import INPUT_FIELD_NAME
|
||||
from langflow.services.session.service import SessionService
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.api.v1.schemas import InputValueRequest, Tweaks
|
||||
from langflow.api.v1.schemas import InputValueRequest
|
||||
|
||||
|
||||
def fix_memory_inputs(langchain_object):
|
||||
|
|
@ -188,7 +189,7 @@ def run_graph(
|
|||
List[RunOutputs]: A list of RunOutputs objects representing the outputs of the graph.
|
||||
|
||||
"""
|
||||
inputs = [InputValueRequest(components=[], input_value=input_value, type=input_type)]
|
||||
inputs = [InputValue(components=[], input_value=input_value, type=input_type)]
|
||||
if output_component:
|
||||
outputs = [output_component]
|
||||
else:
|
||||
|
|
@ -245,7 +246,7 @@ def apply_tweaks(node: Dict[str, Any], node_tweaks: Dict[str, Any]) -> None:
|
|||
logger.warning(f"Node {node.get('id')} does not have a tweak named {tweak_name}")
|
||||
continue
|
||||
if tweak_name in template_data:
|
||||
key = tweak_name if tweak_name == "file_path" else "value"
|
||||
key = "file_path" if template_data[tweak_name]["type"] == "file" else "value"
|
||||
template_data[tweak_name][key] = tweak_value
|
||||
|
||||
|
||||
|
|
|
|||
43
src/backend/base/langflow/schema/graph.py
Normal file
43
src/backend/base/langflow/schema/graph.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from langflow.schema.schema import InputType
|
||||
from pydantic import BaseModel, Field, RootModel
|
||||
|
||||
|
||||
class InputValue(BaseModel):
|
||||
components: Optional[List[str]] = []
|
||||
input_value: Optional[str] = None
|
||||
type: Optional[InputType] = Field(
|
||||
"any",
|
||||
description="Defines on which components the input value should be applied. 'any' applies to all input components.",
|
||||
)
|
||||
|
||||
|
||||
class Tweaks(RootModel):
|
||||
root: dict[str, Union[str, dict[str, str]]] = Field(
|
||||
description="A dictionary of tweaks to adjust the flow's execution. Allows customizing flow behavior dynamically. All tweaks are overridden by the input values.",
|
||||
)
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"parameter_name": "value",
|
||||
"Component Name": {"parameter_name": "value"},
|
||||
"component_id": {"parameter_name": "value"},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# This should behave like a dict
|
||||
def __getitem__(self, key):
|
||||
return self.root[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.root[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.root[key]
|
||||
|
||||
def items(self):
|
||||
return self.root.items()
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import asyncio
|
||||
import logging
|
||||
import signal
|
||||
|
||||
from gunicorn import glogging
|
||||
from gunicorn import glogging # type: ignore
|
||||
from gunicorn.app.base import BaseApplication # type: ignore
|
||||
from uvicorn.workers import UvicornWorker
|
||||
|
||||
|
|
@ -10,6 +12,21 @@ from langflow.utils.logger import InterceptHandler # type: ignore
|
|||
class LangflowUvicornWorker(UvicornWorker):
|
||||
CONFIG_KWARGS = {"loop": "asyncio"}
|
||||
|
||||
def _install_sigint_handler(self) -> None:
|
||||
"""Install a SIGQUIT handler on workers.
|
||||
|
||||
- https://github.com/encode/uvicorn/issues/1116
|
||||
- https://github.com/benoitc/gunicorn/issues/2604
|
||||
"""
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.add_signal_handler(signal.SIGINT, self.handle_exit, signal.SIGINT, None)
|
||||
|
||||
async def _serve(self) -> None:
|
||||
# We do this to not log the "Worker (pid:XXXXX) was sent SIGINT"
|
||||
self._install_sigint_handler()
|
||||
await super()._serve()
|
||||
|
||||
|
||||
class Logger(glogging.Logger):
|
||||
"""Implements and overrides the gunicorn logging interface.
|
||||
|
|
@ -35,11 +52,7 @@ class LangflowApplication(BaseApplication):
|
|||
super().__init__()
|
||||
|
||||
def load_config(self):
|
||||
config = {
|
||||
key: value
|
||||
for key, value in self.options.items()
|
||||
if key in self.cfg.settings and value is not None
|
||||
}
|
||||
config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None}
|
||||
for key, value in config.items():
|
||||
self.cfg.set(key.lower(), value)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ from starlette.websockets import WebSocket
|
|||
|
||||
from langflow.services.database.models.api_key.crud import check_key
|
||||
from langflow.services.database.models.api_key.model import ApiKey
|
||||
from langflow.services.database.models.user.crud import get_user_by_id, get_user_by_username, update_user_last_login_at
|
||||
from langflow.services.database.models.user.crud import (
|
||||
get_user_by_id,
|
||||
get_user_by_username,
|
||||
update_user_last_login_at,
|
||||
)
|
||||
from langflow.services.database.models.user.model import User
|
||||
from langflow.services.deps import get_session, get_settings_service
|
||||
|
||||
|
|
@ -207,7 +211,7 @@ def create_super_user(
|
|||
return super_user
|
||||
|
||||
|
||||
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
|
||||
def create_user_longterm_token(db: Session = Depends(get_session)) -> tuple[UUID, dict]:
|
||||
settings_service = get_settings_service()
|
||||
username = settings_service.auth_settings.SUPERUSER
|
||||
password = settings_service.auth_settings.SUPERUSER_PASSWORD
|
||||
|
|
@ -227,7 +231,7 @@ def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
|
|||
# Update: last_login_at
|
||||
update_user_last_login_at(super_user.id, db)
|
||||
|
||||
return {
|
||||
return super_user.id, {
|
||||
"access_token": access_token,
|
||||
"refresh_token": None,
|
||||
"token_type": "bearer",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from pydantic import validator
|
||||
from sqlmodel import Field, Relationship, SQLModel
|
||||
from pydantic import field_validator, validator
|
||||
from sqlmodel import Field, Relationship, SQLModel, Column, func, DateTime
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.models.user import User
|
||||
|
|
@ -11,7 +11,9 @@ if TYPE_CHECKING:
|
|||
|
||||
class ApiKeyBase(SQLModel):
|
||||
name: Optional[str] = Field(index=True, nullable=True, default=None)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
created_at: datetime = Field(
|
||||
default=None, sa_column=Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
)
|
||||
last_used_at: Optional[datetime] = Field(default=None, nullable=True)
|
||||
total_uses: int = Field(default=0)
|
||||
is_active: bool = Field(default=True)
|
||||
|
|
@ -33,6 +35,10 @@ class ApiKeyCreate(ApiKeyBase):
|
|||
api_key: Optional[str] = None
|
||||
user_id: Optional[UUID] = None
|
||||
|
||||
@field_validator("created_at", mode="before")
|
||||
def set_created_at(cls, v):
|
||||
return v or datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class UnmaskedApiKeyRead(ApiKeyBase):
|
||||
id: UUID
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
# Path: src/backend/langflow/services/database/models/flow/model.py
|
||||
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import emoji
|
||||
from emoji import purely_emoji # type: ignore
|
||||
from pydantic import field_serializer, field_validator
|
||||
from sqlmodel import JSON, Column, Field, Relationship, SQLModel
|
||||
|
||||
from langflow.interface.custom.attributes import validate_icon
|
||||
from langflow.schema.schema import Record
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -45,12 +46,25 @@ class FlowBase(SQLModel):
|
|||
# emoji pattern in Python
|
||||
if v is None:
|
||||
return v
|
||||
# we are going to use the emoji library to validate the emoji
|
||||
# emojis can be defined using the :emoji_name: syntax
|
||||
|
||||
emoji = validate_icon(v)
|
||||
if not v.startswith(":") and not v.endswith(":"):
|
||||
return v
|
||||
elif not v.startswith(":") or not v.endswith(":"):
|
||||
# emoji should have both starting and ending colons
|
||||
# so if one of them is missing, we will raise
|
||||
raise ValueError(f"Invalid emoji. {v} is not a valid emoji.")
|
||||
|
||||
if purely_emoji(emoji):
|
||||
emoji_value = emoji.emojize(v, variant="emoji_type")
|
||||
if v == emoji_value:
|
||||
warnings.warn(f"Invalid emoji. {v} is not a valid emoji.")
|
||||
icon = v
|
||||
icon = emoji_value
|
||||
|
||||
if purely_emoji(icon):
|
||||
# this is indeed an emoji
|
||||
return emoji
|
||||
return icon
|
||||
# otherwise it should be a valid lucide icon
|
||||
if v is not None and not isinstance(v, str):
|
||||
raise ValueError("Icon must be a string")
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ from datetime import datetime, timezone
|
|||
from typing import TYPE_CHECKING, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Field, Relationship, SQLModel
|
||||
|
||||
from sqlmodel import Column, DateTime, Field, Relationship, SQLModel, func
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.models.user.model import User
|
||||
|
|
@ -26,15 +25,25 @@ class Variable(VariableBase, table=True):
|
|||
description="Unique ID for the variable",
|
||||
)
|
||||
# name is unique per user
|
||||
created_at: datetime = Field(default_factory=utc_now, description="Creation time of the variable")
|
||||
updated_at: Optional[datetime] = Field(None, description="Last update time of the variable")
|
||||
created_at: datetime = Field(
|
||||
default=None,
|
||||
sa_column=Column(DateTime(timezone=True), server_default=func.now(), nullable=True),
|
||||
description="Creation time of the variable",
|
||||
)
|
||||
updated_at: Optional[datetime] = Field(
|
||||
default=None,
|
||||
sa_column=Column(DateTime(timezone=True), nullable=True),
|
||||
description="Last update time of the variable",
|
||||
)
|
||||
# foreign key to user table
|
||||
user_id: UUID = Field(description="User ID associated with this variable", foreign_key="user.id")
|
||||
user: "User" = Relationship(back_populates="variables")
|
||||
|
||||
|
||||
class VariableCreate(VariableBase):
|
||||
type: Optional[str] = Field(None, description="Type of the variable")
|
||||
created_at: Optional[datetime] = Field(default_factory=utc_now, description="Creation time of the variable")
|
||||
|
||||
updated_at: Optional[datetime] = Field(default_factory=utc_now, description="Creation time of the variable")
|
||||
|
||||
|
||||
class VariableRead(SQLModel):
|
||||
|
|
|
|||
|
|
@ -6,16 +6,17 @@ from typing import TYPE_CHECKING
|
|||
import sqlalchemy as sa
|
||||
from alembic import command, util
|
||||
from alembic.config import Config
|
||||
from loguru import logger
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from sqlmodel import Session, SQLModel, create_engine, select, text
|
||||
|
||||
from langflow.services.base import Service
|
||||
from langflow.services.database import models # noqa
|
||||
from langflow.services.database.models.user.crud import get_user_by_username
|
||||
from langflow.services.database.utils import Result, TableResults
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.services.utils import teardown_superuser
|
||||
from loguru import logger
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from sqlmodel import Session, SQLModel, create_engine, select, text
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.engine import Engine
|
||||
|
|
@ -36,10 +37,7 @@ class DatabaseService(Service):
|
|||
def _create_engine(self) -> "Engine":
|
||||
"""Create the engine for the database."""
|
||||
settings_service = get_settings_service()
|
||||
if (
|
||||
settings_service.settings.DATABASE_URL
|
||||
and settings_service.settings.DATABASE_URL.startswith("sqlite")
|
||||
):
|
||||
if settings_service.settings.DATABASE_URL and settings_service.settings.DATABASE_URL.startswith("sqlite"):
|
||||
connect_args = {"check_same_thread": False}
|
||||
else:
|
||||
connect_args = {}
|
||||
|
|
@ -51,9 +49,7 @@ class DatabaseService(Service):
|
|||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if exc_type is not None: # If an exception has been raised
|
||||
logger.error(
|
||||
f"Session rollback because of exception: {exc_type.__name__} {exc_value}"
|
||||
)
|
||||
logger.error(f"Session rollback because of exception: {exc_type.__name__} {exc_value}")
|
||||
self._session.rollback()
|
||||
else:
|
||||
self._session.commit()
|
||||
|
|
@ -70,9 +66,7 @@ class DatabaseService(Service):
|
|||
settings_service = get_settings_service()
|
||||
if settings_service.auth_settings.AUTO_LOGIN:
|
||||
with Session(self.engine) as session:
|
||||
flows = session.exec(
|
||||
select(models.Flow).where(models.Flow.user_id is None)
|
||||
).all()
|
||||
flows = session.exec(select(models.Flow).where(models.Flow.user_id is None)).all()
|
||||
if flows:
|
||||
logger.debug("Migrating flows to default superuser")
|
||||
username = settings_service.auth_settings.SUPERUSER
|
||||
|
|
@ -102,9 +96,7 @@ class DatabaseService(Service):
|
|||
expected_columns = list(model.model_fields.keys())
|
||||
|
||||
try:
|
||||
available_columns = [
|
||||
col["name"] for col in inspector.get_columns(table)
|
||||
]
|
||||
available_columns = [col["name"] for col in inspector.get_columns(table)]
|
||||
except sa.exc.NoSuchTableError:
|
||||
logger.debug(f"Missing table: {table}")
|
||||
return False
|
||||
|
|
@ -168,9 +160,7 @@ class DatabaseService(Service):
|
|||
buffer.write(f"{datetime.now().isoformat()}: Checking migrations\n")
|
||||
command.check(alembic_cfg)
|
||||
except Exception as exc:
|
||||
if isinstance(
|
||||
exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)
|
||||
):
|
||||
if isinstance(exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)):
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
time.sleep(3)
|
||||
|
||||
|
|
@ -206,11 +196,11 @@ class DatabaseService(Service):
|
|||
# This method is used for testing purposes only
|
||||
# We will check that all models are in the database
|
||||
# and that the database is up to date with all columns
|
||||
sql_models = [models.Flow, models.User, models.ApiKey]
|
||||
return [
|
||||
TableResults(sql_model.__tablename__, self.check_table(sql_model))
|
||||
for sql_model in sql_models
|
||||
# get all models that are subclasses of SQLModel
|
||||
sql_models = [
|
||||
model for model in models.__dict__.values() if isinstance(model, type) and issubclass(model, SQLModel)
|
||||
]
|
||||
return [TableResults(sql_model.__tablename__, self.check_table(sql_model)) for sql_model in sql_models]
|
||||
|
||||
def check_table(self, model):
|
||||
results = []
|
||||
|
|
@ -219,9 +209,7 @@ class DatabaseService(Service):
|
|||
expected_columns = list(model.__fields__.keys())
|
||||
available_columns = []
|
||||
try:
|
||||
available_columns = [
|
||||
col["name"] for col in inspector.get_columns(table_name)
|
||||
]
|
||||
available_columns = [col["name"] for col in inspector.get_columns(table_name)]
|
||||
results.append(Result(name=table_name, type="table", success=True))
|
||||
except sa.exc.NoSuchTableError:
|
||||
logger.error(f"Missing table: {table_name}")
|
||||
|
|
@ -252,9 +240,7 @@ class DatabaseService(Service):
|
|||
try:
|
||||
table.create(self.engine, checkfirst=True)
|
||||
except OperationalError as oe:
|
||||
logger.warning(
|
||||
f"Table {table} already exists, skipping. Exception: {oe}"
|
||||
)
|
||||
logger.warning(f"Table {table} already exists, skipping. Exception: {oe}")
|
||||
except Exception as exc:
|
||||
logger.error(f"Error creating table {table}: {exc}")
|
||||
raise RuntimeError(f"Error creating table {table}") from exc
|
||||
|
|
@ -266,9 +252,7 @@ class DatabaseService(Service):
|
|||
if table not in table_names:
|
||||
logger.error("Something went wrong creating the database and tables.")
|
||||
logger.error("Please check your database settings.")
|
||||
raise RuntimeError(
|
||||
"Something went wrong creating the database and tables."
|
||||
)
|
||||
raise RuntimeError("Something went wrong creating the database and tables.")
|
||||
|
||||
logger.debug("Database and tables created successfully")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from contextlib import contextmanager
|
||||
from typing import TYPE_CHECKING, Generator
|
||||
|
||||
from langflow.services import ServiceType, service_manager
|
||||
from langflow.services.schema import ServiceType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlmodel import Session
|
||||
|
|
@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|||
from langflow.services.variable.service import VariableService
|
||||
|
||||
|
||||
def get_service(service_type: ServiceType):
|
||||
def get_service(service_type: ServiceType, default=None):
|
||||
"""
|
||||
Retrieves the service instance for the given service type.
|
||||
|
||||
|
|
@ -32,7 +32,13 @@ def get_service(service_type: ServiceType):
|
|||
Any: The service instance.
|
||||
|
||||
"""
|
||||
return service_manager.get(service_type) # type: ignore
|
||||
from langflow.services.manager import service_manager
|
||||
|
||||
if not service_manager.factories:
|
||||
#! This is a workaround to ensure that the service manager is initialized
|
||||
#! Not optimal, but it works for now
|
||||
service_manager.register_factories()
|
||||
return service_manager.get(service_type, default) # type: ignore
|
||||
|
||||
|
||||
def get_state_service() -> "StateService":
|
||||
|
|
@ -42,7 +48,9 @@ def get_state_service() -> "StateService":
|
|||
Returns:
|
||||
The StateService instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.STATE_SERVICE) # type: ignore
|
||||
from langflow.services.state.factory import StateServiceFactory
|
||||
|
||||
return get_service(ServiceType.STATE_SERVICE, StateServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_socket_service() -> "SocketIOService":
|
||||
|
|
@ -52,7 +60,7 @@ def get_socket_service() -> "SocketIOService":
|
|||
Returns:
|
||||
SocketIOService: The SocketIOService instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.SOCKETIO_SERVICE) # type: ignore
|
||||
return get_service(ServiceType.SOCKETIO_SERVICE) # type: ignore
|
||||
|
||||
|
||||
def get_storage_service() -> "StorageService":
|
||||
|
|
@ -62,7 +70,9 @@ def get_storage_service() -> "StorageService":
|
|||
Returns:
|
||||
The storage service instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.STORAGE_SERVICE) # type: ignore
|
||||
from langflow.services.storage.factory import StorageServiceFactory
|
||||
|
||||
return get_service(ServiceType.STORAGE_SERVICE, default=StorageServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_variable_service() -> "VariableService":
|
||||
|
|
@ -73,7 +83,9 @@ def get_variable_service() -> "VariableService":
|
|||
The VariableService instance.
|
||||
|
||||
"""
|
||||
return service_manager.get(ServiceType.VARIABLE_SERVICE) # type: ignore
|
||||
from langflow.services.variable.factory import VariableServiceFactory
|
||||
|
||||
return get_service(ServiceType.VARIABLE_SERVICE, VariableServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_plugins_service() -> "PluginService":
|
||||
|
|
@ -83,7 +95,7 @@ def get_plugins_service() -> "PluginService":
|
|||
Returns:
|
||||
PluginService: The PluginService instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.PLUGIN_SERVICE) # type: ignore
|
||||
return get_service(ServiceType.PLUGIN_SERVICE) # type: ignore
|
||||
|
||||
|
||||
def get_settings_service() -> "SettingsService":
|
||||
|
|
@ -98,14 +110,9 @@ def get_settings_service() -> "SettingsService":
|
|||
Raises:
|
||||
ValueError: If the service cannot be retrieved or initialized.
|
||||
"""
|
||||
try:
|
||||
return service_manager.get(ServiceType.SETTINGS_SERVICE) # type: ignore
|
||||
except ValueError:
|
||||
# initialize settings service
|
||||
from langflow.services.manager import initialize_settings_service
|
||||
from langflow.services.settings.factory import SettingsServiceFactory
|
||||
|
||||
initialize_settings_service()
|
||||
return service_manager.get(ServiceType.SETTINGS_SERVICE) # type: ignore
|
||||
return get_service(ServiceType.SETTINGS_SERVICE, SettingsServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_db_service() -> "DatabaseService":
|
||||
|
|
@ -116,7 +123,9 @@ def get_db_service() -> "DatabaseService":
|
|||
The DatabaseService instance.
|
||||
|
||||
"""
|
||||
return service_manager.get(ServiceType.DATABASE_SERVICE) # type: ignore
|
||||
from langflow.services.database.factory import DatabaseServiceFactory
|
||||
|
||||
return get_service(ServiceType.DATABASE_SERVICE, DatabaseServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_session() -> Generator["Session", None, None]:
|
||||
|
|
@ -165,7 +174,9 @@ def get_cache_service() -> "CacheService":
|
|||
Returns:
|
||||
The cache service instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.CACHE_SERVICE) # type: ignore
|
||||
from langflow.services.cache.factory import CacheServiceFactory
|
||||
|
||||
return get_service(ServiceType.CACHE_SERVICE, CacheServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_session_service() -> "SessionService":
|
||||
|
|
@ -175,7 +186,9 @@ def get_session_service() -> "SessionService":
|
|||
Returns:
|
||||
The session service instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.SESSION_SERVICE) # type: ignore
|
||||
from langflow.services.session.factory import SessionServiceFactory
|
||||
|
||||
return get_service(ServiceType.SESSION_SERVICE, SessionServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_monitor_service() -> "MonitorService":
|
||||
|
|
@ -185,7 +198,9 @@ def get_monitor_service() -> "MonitorService":
|
|||
Returns:
|
||||
MonitorService: The MonitorService instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.MONITOR_SERVICE) # type: ignore
|
||||
from langflow.services.monitor.factory import MonitorServiceFactory
|
||||
|
||||
return get_service(ServiceType.MONITOR_SERVICE, MonitorServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_task_service() -> "TaskService":
|
||||
|
|
@ -196,7 +211,9 @@ def get_task_service() -> "TaskService":
|
|||
The TaskService instance.
|
||||
|
||||
"""
|
||||
return service_manager.get(ServiceType.TASK_SERVICE) # type: ignore
|
||||
from langflow.services.task.factory import TaskServiceFactory
|
||||
|
||||
return get_service(ServiceType.TASK_SERVICE, TaskServiceFactory()) # type: ignore
|
||||
|
||||
|
||||
def get_chat_service() -> "ChatService":
|
||||
|
|
@ -206,7 +223,7 @@ def get_chat_service() -> "ChatService":
|
|||
Returns:
|
||||
ChatService: The chat service instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.CHAT_SERVICE) # type: ignore
|
||||
return get_service(ServiceType.CHAT_SERVICE) # type: ignore
|
||||
|
||||
|
||||
def get_store_service() -> "StoreService":
|
||||
|
|
@ -216,4 +233,4 @@ def get_store_service() -> "StoreService":
|
|||
Returns:
|
||||
StoreService: The StoreService instance.
|
||||
"""
|
||||
return service_manager.get(ServiceType.STORE_SERVICE) # type: ignore
|
||||
return get_service(ServiceType.STORE_SERVICE) # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
from typing import TYPE_CHECKING, Dict
|
||||
import importlib
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.base import Service
|
||||
|
||||
from langflow.services.factory import ServiceFactory
|
||||
from langflow.services.schema import ServiceType
|
||||
|
||||
|
||||
class NoFactoryRegisteredError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceManager:
|
||||
"""
|
||||
Manages the creation of different services.
|
||||
|
|
@ -16,6 +23,15 @@ class ServiceManager:
|
|||
def __init__(self):
|
||||
self.services: Dict[str, "Service"] = {}
|
||||
self.factories = {}
|
||||
self.register_factories()
|
||||
|
||||
def register_factories(self):
|
||||
for factory in self.get_factories():
|
||||
try:
|
||||
self.register_factory(factory)
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
logger.error(f"Error initializing {factory}: {exc}")
|
||||
|
||||
def register_factory(
|
||||
self,
|
||||
|
|
@ -28,24 +44,28 @@ class ServiceManager:
|
|||
service_name = service_factory.service_class.name
|
||||
self.factories[service_name] = service_factory
|
||||
|
||||
def get(self, service_name: "ServiceType") -> "Service":
|
||||
def get(self, service_name: "ServiceType", default: Optional["ServiceFactory"] = None) -> "Service":
|
||||
"""
|
||||
Get (or create) a service by its name.
|
||||
"""
|
||||
|
||||
if service_name not in self.services:
|
||||
self._create_service(service_name)
|
||||
self._create_service(service_name, default)
|
||||
|
||||
return self.services[service_name]
|
||||
|
||||
def _create_service(self, service_name: "ServiceType"):
|
||||
def _create_service(self, service_name: "ServiceType", default: Optional["ServiceFactory"] = None):
|
||||
"""
|
||||
Create a new service given its name, handling dependencies.
|
||||
"""
|
||||
logger.debug(f"Create service {service_name}")
|
||||
self._validate_service_creation(service_name)
|
||||
self._validate_service_creation(service_name, default)
|
||||
|
||||
# Create dependencies first
|
||||
factory = self.factories.get(service_name)
|
||||
if factory is None and default is not None:
|
||||
self.register_factory(default)
|
||||
factory = default
|
||||
for dependency in factory.dependencies:
|
||||
if dependency not in self.services:
|
||||
self._create_service(dependency)
|
||||
|
|
@ -57,12 +77,12 @@ class ServiceManager:
|
|||
self.services[service_name] = self.factories[service_name].create(**dependent_services)
|
||||
self.services[service_name].set_ready()
|
||||
|
||||
def _validate_service_creation(self, service_name: "ServiceType"):
|
||||
def _validate_service_creation(self, service_name: "ServiceType", default: Optional["ServiceFactory"] = None):
|
||||
"""
|
||||
Validate whether the service can be created.
|
||||
"""
|
||||
if service_name not in self.factories:
|
||||
raise ValueError(f"No factory registered for the service class '{service_name.name}'")
|
||||
if service_name not in self.factories and default is None:
|
||||
raise NoFactoryRegisteredError(f"No factory registered for the service class '{service_name.name}'")
|
||||
|
||||
def update(self, service_name: "ServiceType"):
|
||||
"""
|
||||
|
|
@ -88,6 +108,34 @@ class ServiceManager:
|
|||
self.services = {}
|
||||
self.factories = {}
|
||||
|
||||
@staticmethod
|
||||
def get_factories():
|
||||
from langflow.services.factory import ServiceFactory
|
||||
from langflow.services.schema import ServiceType
|
||||
|
||||
service_names = [ServiceType(service_type).value.replace("_service", "") for service_type in ServiceType]
|
||||
base_module = "langflow.services"
|
||||
factories = []
|
||||
|
||||
for name in service_names:
|
||||
try:
|
||||
module_name = f"{base_module}.{name}.factory"
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
# Find all classes in the module that are subclasses of ServiceFactory
|
||||
for name, obj in inspect.getmembers(module, inspect.isclass):
|
||||
if issubclass(obj, ServiceFactory) and obj is not ServiceFactory:
|
||||
factories.append(obj())
|
||||
break
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise RuntimeError(
|
||||
f"Could not initialize services. Please check your settings. Error in {name}."
|
||||
) from exc
|
||||
|
||||
return factories
|
||||
|
||||
|
||||
service_manager = ServiceManager()
|
||||
|
||||
|
|
@ -106,9 +154,7 @@ def initialize_session_service():
|
|||
Initialize the session manager.
|
||||
"""
|
||||
from langflow.services.cache import factory as cache_factory
|
||||
from langflow.services.session import (
|
||||
factory as session_service_factory,
|
||||
) # type: ignore
|
||||
from langflow.services.session import factory as session_service_factory # type: ignore
|
||||
|
||||
initialize_settings_service()
|
||||
|
||||
|
|
|
|||
|
|
@ -19,5 +19,5 @@ class ServiceType(str, Enum):
|
|||
VARIABLE_SERVICE = "variable_service"
|
||||
STORAGE_SERVICE = "storage_service"
|
||||
MONITOR_SERVICE = "monitor_service"
|
||||
SOCKETIO_SERVICE = "socket_service"
|
||||
# SOCKETIO_SERVICE = "socket_service"
|
||||
STATE_SERVICE = "state_service"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Coroutine, Optional
|
||||
|
||||
from langflow.interface.run import build_sorted_vertices
|
||||
from langflow.services.base import Service
|
||||
from langflow.services.cache.base import CacheService
|
||||
from langflow.services.session.utils import compute_dict_hash, session_id_generator
|
||||
|
|
@ -25,8 +24,10 @@ class SessionService(Service):
|
|||
if data_graph is None:
|
||||
return (None, None)
|
||||
# If not cached, build the graph and cache it
|
||||
graph, artifacts = await build_sorted_vertices(data_graph, flow_id)
|
||||
from langflow.graph.graph.base import Graph
|
||||
|
||||
graph = Graph.from_payload(data_graph, flow_id=flow_id)
|
||||
artifacts: dict = {}
|
||||
await self.cache_service.set(key, (graph, artifacts))
|
||||
|
||||
return graph, artifacts
|
||||
|
|
|
|||
|
|
@ -3,18 +3,60 @@ import json
|
|||
import os
|
||||
from pathlib import Path
|
||||
from shutil import copy2
|
||||
from typing import List, Optional
|
||||
from typing import Any, List, Optional, Tuple, Type
|
||||
|
||||
import orjson
|
||||
import yaml
|
||||
from loguru import logger
|
||||
from pydantic import field_validator, validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from pydantic.fields import FieldInfo
|
||||
from pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict
|
||||
|
||||
from langflow.services.settings.constants import VARIABLES_TO_GET_FROM_ENVIRONMENT
|
||||
|
||||
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
|
||||
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
|
||||
|
||||
|
||||
def is_list_of_any(field: FieldInfo) -> bool:
|
||||
"""
|
||||
Check if the given field is a list or an optional list of any type.
|
||||
|
||||
Args:
|
||||
field (FieldInfo): The field to be checked.
|
||||
|
||||
Returns:
|
||||
bool: True if the field is a list or a list of any type, False otherwise.
|
||||
"""
|
||||
if field.annotation is None:
|
||||
return False
|
||||
try:
|
||||
if hasattr(field.annotation, "__args__"):
|
||||
union_args = field.annotation.__args__
|
||||
else:
|
||||
union_args = []
|
||||
|
||||
return field.annotation.__origin__ == list or any(
|
||||
arg.__origin__ == list for arg in union_args if hasattr(arg, "__origin__")
|
||||
)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
|
||||
class MyCustomSource(EnvSettingsSource):
|
||||
def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
|
||||
# allow comma-separated list parsing
|
||||
|
||||
# fieldInfo contains the annotation of the field
|
||||
if is_list_of_any(field):
|
||||
if isinstance(value, str):
|
||||
value = value.split(",")
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
|
||||
return super().prepare_field_value(field_name, field, value, value_is_complex)
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
CHAINS: dict = {}
|
||||
AGENTS: dict = {}
|
||||
|
|
@ -58,15 +100,20 @@ class Settings(BaseSettings):
|
|||
|
||||
STORE: Optional[bool] = True
|
||||
STORE_URL: Optional[str] = "https://api.langflow.store"
|
||||
DOWNLOAD_WEBHOOK_URL: Optional[
|
||||
str
|
||||
] = "https://api.langflow.store/flows/trigger/ec611a61-8460-4438-b187-a4f65e5559d4"
|
||||
DOWNLOAD_WEBHOOK_URL: Optional[str] = (
|
||||
"https://api.langflow.store/flows/trigger/ec611a61-8460-4438-b187-a4f65e5559d4"
|
||||
)
|
||||
LIKE_WEBHOOK_URL: Optional[str] = "https://api.langflow.store/flows/trigger/64275852-ec00-45c1-984e-3bff814732da"
|
||||
|
||||
STORAGE_TYPE: str = "local"
|
||||
|
||||
CELERY_ENABLED: bool = False
|
||||
|
||||
store_environment_variables: bool = True
|
||||
"""Whether to store environment variables as Global Variables in the database."""
|
||||
variables_to_get_from_environment: list[str] = VARIABLES_TO_GET_FROM_ENVIRONMENT
|
||||
"""List of environment variables to get from the environment and store in the database."""
|
||||
|
||||
@validator("CONFIG_DIR", pre=True, allow_reuse=True)
|
||||
def set_langflow_dir(cls, value):
|
||||
if not value:
|
||||
|
|
@ -104,21 +151,50 @@ class Settings(BaseSettings):
|
|||
# if there is a database in that location
|
||||
if not values["CONFIG_DIR"]:
|
||||
raise ValueError("CONFIG_DIR not set, please set it or provide a DATABASE_URL")
|
||||
from langflow.version import is_pre_release # type: ignore
|
||||
|
||||
new_path = f"{values['CONFIG_DIR']}/langflow.db"
|
||||
if Path("./langflow.db").exists():
|
||||
pre_db_file_name = "langflow-pre.db"
|
||||
db_file_name = "langflow.db"
|
||||
new_pre_path = f"{values['CONFIG_DIR']}/{pre_db_file_name}"
|
||||
new_path = f"{values['CONFIG_DIR']}/{db_file_name}"
|
||||
final_path = None
|
||||
if is_pre_release:
|
||||
if Path(new_pre_path).exists():
|
||||
final_path = new_pre_path
|
||||
elif Path(new_path).exists():
|
||||
# We need to copy the current db to the new location
|
||||
logger.debug("Copying existing database to new location")
|
||||
copy2(new_path, new_pre_path)
|
||||
logger.debug(f"Copied existing database to {new_pre_path}")
|
||||
elif Path(f"./{db_file_name}").exists():
|
||||
logger.debug("Copying existing database to new location")
|
||||
copy2(f"./{db_file_name}", new_pre_path)
|
||||
logger.debug(f"Copied existing database to {new_pre_path}")
|
||||
else:
|
||||
logger.debug(f"Database already exists at {new_pre_path}, using it")
|
||||
final_path = new_pre_path
|
||||
else:
|
||||
if Path(new_path).exists():
|
||||
logger.debug(f"Database already exists at {new_path}, using it")
|
||||
else:
|
||||
final_path = new_path
|
||||
elif Path("./{db_file_name}").exists():
|
||||
try:
|
||||
logger.debug("Copying existing database to new location")
|
||||
copy2("./langflow.db", new_path)
|
||||
copy2("./{db_file_name}", new_path)
|
||||
logger.debug(f"Copied existing database to {new_path}")
|
||||
except Exception:
|
||||
logger.error("Failed to copy database, using default path")
|
||||
new_path = "./langflow.db"
|
||||
new_path = "./{db_file_name}"
|
||||
else:
|
||||
final_path = new_path
|
||||
|
||||
value = f"sqlite:///{new_path}"
|
||||
if final_path is None:
|
||||
if is_pre_release:
|
||||
final_path = new_pre_path
|
||||
else:
|
||||
final_path = new_path
|
||||
|
||||
value = f"sqlite:///{final_path}"
|
||||
|
||||
return value
|
||||
|
||||
|
|
@ -149,14 +225,6 @@ class Settings(BaseSettings):
|
|||
|
||||
model_config = SettingsConfigDict(validate_assignment=True, extra="ignore", env_prefix="LANGFLOW_")
|
||||
|
||||
# @model_validator()
|
||||
# @classmethod
|
||||
# def validate_lists(cls, values):
|
||||
# for key, value in values.items():
|
||||
# if key != "dev" and not value:
|
||||
# values[key] = []
|
||||
# return values
|
||||
|
||||
def update_from_yaml(self, file_path: str, dev: bool = False):
|
||||
new_settings = load_settings_from_yaml(file_path)
|
||||
self.CHAINS = new_settings.CHAINS or {}
|
||||
|
|
@ -209,6 +277,17 @@ class Settings(BaseSettings):
|
|||
logger.debug(f"Updated {key}")
|
||||
logger.debug(f"{key}: {getattr(self, key)}")
|
||||
|
||||
@classmethod
|
||||
def settings_customise_sources(
|
||||
cls,
|
||||
settings_cls: Type[BaseSettings],
|
||||
init_settings: PydanticBaseSettingsSource,
|
||||
env_settings: PydanticBaseSettingsSource,
|
||||
dotenv_settings: PydanticBaseSettingsSource,
|
||||
file_secret_settings: PydanticBaseSettingsSource,
|
||||
) -> Tuple[PydanticBaseSettingsSource, ...]:
|
||||
return (MyCustomSource(settings_cls),)
|
||||
|
||||
|
||||
def save_settings_to_yaml(settings: Settings, file_path: str):
|
||||
with open(file_path, "w") as f:
|
||||
|
|
|
|||
|
|
@ -1,2 +1,21 @@
|
|||
DEFAULT_SUPERUSER = "langflow"
|
||||
DEFAULT_SUPERUSER_PASSWORD = "langflow"
|
||||
VARIABLES_TO_GET_FROM_ENVIRONMENT = [
|
||||
"OPENAI_API_KEY",
|
||||
"ANTHROPIC_API_KEY",
|
||||
"GOOGLE_API_KEY",
|
||||
"AZURE_OPENAI_API_KEY",
|
||||
"AZURE_OPENAI_API_VERSION",
|
||||
"AZURE_OPENAI_API_INSTANCE_NAME",
|
||||
"AZURE_OPENAI_API_DEPLOYMENT_NAME",
|
||||
"AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME",
|
||||
"ASTRA_DB_APPLICATION_TOKEN",
|
||||
"ASTRA_DB_API_ENDPOINT",
|
||||
"COHERE_API_KEY",
|
||||
"HUGGINGFACEHUB_API_TOKEN",
|
||||
"SEARCHAPI_API_KEY",
|
||||
"SERPAPI_API_KEY",
|
||||
"VECTARA_CUSTOMER_ID",
|
||||
"VECTARA_CORPUS_ID",
|
||||
"VECTARA_API_KEY",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,41 +1,13 @@
|
|||
import importlib
|
||||
import inspect
|
||||
|
||||
from loguru import logger
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from langflow.services.auth.utils import create_super_user, verify_password
|
||||
from langflow.services.cache.factory import CacheServiceFactory
|
||||
from langflow.services.database.utils import initialize_database
|
||||
from langflow.services.factory import ServiceFactory
|
||||
from langflow.services.manager import service_manager
|
||||
from langflow.services.schema import ServiceType
|
||||
from langflow.services.settings.constants import DEFAULT_SUPERUSER, DEFAULT_SUPERUSER_PASSWORD
|
||||
from langflow.services.socket.utils import set_socketio_server
|
||||
|
||||
from .deps import get_db_service, get_session, get_settings_service
|
||||
|
||||
|
||||
def get_factories():
|
||||
service_names = [ServiceType(service_type).value.replace("_service", "") for service_type in ServiceType]
|
||||
base_module = "langflow.services"
|
||||
factories = []
|
||||
|
||||
for name in service_names:
|
||||
try:
|
||||
module_name = f"{base_module}.{name}.factory"
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
# Find all classes in the module that are subclasses of ServiceFactory
|
||||
for name, obj in inspect.getmembers(module, inspect.isclass):
|
||||
if issubclass(obj, ServiceFactory) and obj is not ServiceFactory:
|
||||
factories.append(obj())
|
||||
break
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
raise RuntimeError(f"Could not initialize services. Please check your settings. Error in {name}.") from exc
|
||||
|
||||
return factories
|
||||
from .deps import get_db_service, get_service, get_session, get_settings_service
|
||||
|
||||
|
||||
def get_or_create_super_user(session: Session, username, password, is_default):
|
||||
|
|
@ -145,6 +117,8 @@ def teardown_services():
|
|||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
try:
|
||||
from langflow.services.manager import service_manager
|
||||
|
||||
service_manager.teardown()
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
|
|
@ -156,7 +130,7 @@ def initialize_settings_service():
|
|||
"""
|
||||
from langflow.services.settings import factory as settings_factory
|
||||
|
||||
service_manager.register_factory(settings_factory.SettingsServiceFactory())
|
||||
get_service(ServiceType.SETTINGS_SERVICE, settings_factory.SettingsServiceFactory())
|
||||
|
||||
|
||||
def initialize_session_service():
|
||||
|
|
@ -168,11 +142,13 @@ def initialize_session_service():
|
|||
|
||||
initialize_settings_service()
|
||||
|
||||
service_manager.register_factory(
|
||||
get_service(
|
||||
ServiceType.CACHE_SERVICE,
|
||||
cache_factory.CacheServiceFactory(),
|
||||
)
|
||||
|
||||
service_manager.register_factory(
|
||||
get_service(
|
||||
ServiceType.SESSION_SERVICE,
|
||||
session_service_factory.SessionServiceFactory(),
|
||||
)
|
||||
|
||||
|
|
@ -181,27 +157,17 @@ def initialize_services(fix_migration: bool = False, socketio_server=None):
|
|||
"""
|
||||
Initialize all the services needed.
|
||||
"""
|
||||
for factory in get_factories():
|
||||
try:
|
||||
service_manager.register_factory(factory)
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
logger.error(f"Error initializing {factory}: {exc}")
|
||||
|
||||
# Test cache connection
|
||||
service_manager.get(ServiceType.CACHE_SERVICE)
|
||||
get_service(ServiceType.CACHE_SERVICE, default=CacheServiceFactory())
|
||||
# Setup the superuser
|
||||
try:
|
||||
initialize_database(fix_migration=fix_migration)
|
||||
except Exception as exc:
|
||||
logger.error(exc)
|
||||
raise exc
|
||||
setup_superuser(service_manager.get(ServiceType.SETTINGS_SERVICE), next(get_session()))
|
||||
setup_superuser(get_service(ServiceType.SETTINGS_SERVICE), next(get_session()))
|
||||
try:
|
||||
get_db_service().migrate_flows_if_auto_login()
|
||||
except Exception as exc:
|
||||
logger.error(f"Error migrating flows: {exc}")
|
||||
raise RuntimeError("Error migrating flows") from exc
|
||||
|
||||
# Initialize the SocketIO service
|
||||
set_socketio_server(socketio_server)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import os
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import Depends
|
||||
from loguru import logger
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from langflow.services.auth import utils as auth_utils
|
||||
from langflow.services.base import Service
|
||||
from langflow.services.database.models.variable.model import Variable
|
||||
from langflow.services.database.models.variable.model import Variable, VariableCreate
|
||||
from langflow.services.deps import get_session
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -19,7 +21,28 @@ class VariableService(Service):
|
|||
def __init__(self, settings_service: "SettingsService"):
|
||||
self.settings_service = settings_service
|
||||
|
||||
def get_variable(self, user_id: Union[UUID, str], name: str, session: Session = Depends(get_session)) -> str:
|
||||
def initialize_user_variables(self, user_id: Union[UUID, str], session: Session = Depends(get_session)):
|
||||
# Check for environment variables that should be stored in the database
|
||||
should_or_should_not = "Should" if self.settings_service.settings.store_environment_variables else "Should not"
|
||||
logger.info(f"{should_or_should_not} store environment variables in the database.")
|
||||
if self.settings_service.settings.store_environment_variables:
|
||||
for var in self.settings_service.settings.variables_to_get_from_environment:
|
||||
if var in os.environ:
|
||||
logger.debug(f"Creating {var} variable from environment.")
|
||||
try:
|
||||
self.create_variable(user_id, var, os.environ[var], _type="Credential", session=session)
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating {var} variable: {e}")
|
||||
|
||||
else:
|
||||
logger.info("Skipping environment variable storage.")
|
||||
|
||||
def get_variable(
|
||||
self,
|
||||
user_id: Union[UUID, str],
|
||||
name: str,
|
||||
session: Session = Depends(get_session),
|
||||
) -> str:
|
||||
# we get the credential from the database
|
||||
# credential = session.query(Variable).filter(Variable.user_id == user_id, Variable.name == name).first()
|
||||
variable = session.exec(select(Variable).where(Variable.user_id == user_id, Variable.name == name)).first()
|
||||
|
|
@ -34,7 +57,11 @@ class VariableService(Service):
|
|||
return [variable.name for variable in variables]
|
||||
|
||||
def update_variable(
|
||||
self, user_id: Union[UUID, str], name: str, value: str, session: Session = Depends(get_session)
|
||||
self,
|
||||
user_id: Union[UUID, str],
|
||||
name: str,
|
||||
value: str,
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
variable = session.exec(select(Variable).where(Variable.user_id == user_id, Variable.name == name)).first()
|
||||
if not variable:
|
||||
|
|
@ -46,7 +73,12 @@ class VariableService(Service):
|
|||
session.refresh(variable)
|
||||
return variable
|
||||
|
||||
def delete_variable(self, user_id: Union[UUID, str], name: str, session: Session = Depends(get_session)):
|
||||
def delete_variable(
|
||||
self,
|
||||
user_id: Union[UUID, str],
|
||||
name: str,
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
variable = session.exec(select(Variable).where(Variable.user_id == user_id, Variable.name == name)).first()
|
||||
if not variable:
|
||||
raise ValueError(f"{name} variable not found.")
|
||||
|
|
@ -55,11 +87,19 @@ class VariableService(Service):
|
|||
return variable
|
||||
|
||||
def create_variable(
|
||||
self, user_id: Union[UUID, str], name: str, value: str, session: Session = Depends(get_session)
|
||||
self,
|
||||
user_id: Union[UUID, str],
|
||||
name: str,
|
||||
value: str,
|
||||
_type: str = "Generic",
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
variable = Variable(
|
||||
user_id=user_id, name=name, value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service)
|
||||
variable_base = VariableCreate(
|
||||
name=name,
|
||||
type=_type,
|
||||
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
||||
)
|
||||
variable = Variable.model_validate(variable_base, from_attributes=True, update={"user_id": user_id})
|
||||
session.add(variable)
|
||||
session.commit()
|
||||
session.refresh(variable)
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ class LLMFrontendNode(FrontendNode):
|
|||
field.info = OPENAI_API_BASE_INFO
|
||||
|
||||
def add_extra_base_classes(self) -> None:
|
||||
if "BaseLLM" not in self.base_classes:
|
||||
self.base_classes.append("BaseLLM")
|
||||
if "BaseLanguageModel" not in self.base_classes:
|
||||
self.base_classes.append("BaseLanguageModel")
|
||||
|
||||
@staticmethod
|
||||
def format_azure_field(field: TemplateField):
|
||||
|
|
|
|||
|
|
@ -25,11 +25,10 @@ def patching(record):
|
|||
record["extra"]["serialized"] = serialize(record)
|
||||
|
||||
|
||||
def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None):
|
||||
if (
|
||||
os.getenv("LANGFLOW_LOG_LEVEL", "").upper() in VALID_LOG_LEVELS
|
||||
and log_level is None
|
||||
):
|
||||
def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None, disable: Optional[bool] = False):
|
||||
if disable and log_level is None and log_file is None:
|
||||
logger.disable("langflow")
|
||||
if os.getenv("LANGFLOW_LOG_LEVEL", "").upper() in VALID_LOG_LEVELS and log_level is None:
|
||||
log_level = os.getenv("LANGFLOW_LOG_LEVEL")
|
||||
if log_level is None:
|
||||
log_level = "ERROR"
|
||||
|
|
@ -77,11 +76,7 @@ def configure(log_level: Optional[str] = None, log_file: Optional[Path] = None):
|
|||
|
||||
|
||||
def setup_uvicorn_logger():
|
||||
loggers = (
|
||||
logging.getLogger(name)
|
||||
for name in logging.root.manager.loggerDict
|
||||
if name.startswith("uvicorn.")
|
||||
)
|
||||
loggers = (logging.getLogger(name) for name in logging.root.manager.loggerDict if name.startswith("uvicorn."))
|
||||
for uvicorn_logger in loggers:
|
||||
uvicorn_logger.handlers = []
|
||||
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
|
||||
|
|
@ -111,6 +106,4 @@ class InterceptHandler(logging.Handler):
|
|||
frame = frame.f_back
|
||||
depth += 1
|
||||
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(
|
||||
level, record.getMessage()
|
||||
)
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ import importlib
|
|||
import inspect
|
||||
import re
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from docstring_parser import parse
|
||||
|
||||
from langflow.schema.schema import Record
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS
|
||||
from langflow.utils import constants
|
||||
from langflow.utils.logger import logger
|
||||
|
||||
|
||||
def unescape_string(s: str):
|
||||
|
|
@ -66,54 +68,6 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct
|
|||
}
|
||||
|
||||
|
||||
def build_template_from_class(name: str, type_to_cls_dict: Dict, add_function: bool = False):
|
||||
classes = [item.__name__ for item in type_to_cls_dict.values()]
|
||||
|
||||
# Raise error if name is not in chains
|
||||
if name not in classes:
|
||||
raise ValueError(f"{name} not found.")
|
||||
|
||||
for _type, v in type_to_cls_dict.items():
|
||||
if v.__name__ == name:
|
||||
_class = v
|
||||
|
||||
# Get the docstring
|
||||
docs = parse(_class.__doc__)
|
||||
|
||||
variables = {"_type": _type}
|
||||
|
||||
if "__fields__" in _class.__dict__:
|
||||
for class_field_items, value in _class.__fields__.items():
|
||||
if class_field_items in ["callback_manager"]:
|
||||
continue
|
||||
variables[class_field_items] = {}
|
||||
for name_, value_ in value.__repr_args__():
|
||||
if name_ == "default_factory":
|
||||
try:
|
||||
variables[class_field_items]["default"] = get_default_factory(
|
||||
module=_class.__base__.__module__,
|
||||
function=value_,
|
||||
)
|
||||
except Exception:
|
||||
variables[class_field_items]["default"] = None
|
||||
elif name_ not in ["name"]:
|
||||
variables[class_field_items][name_] = value_
|
||||
|
||||
variables[class_field_items]["placeholder"] = (
|
||||
docs.params[class_field_items] if class_field_items in docs.params else ""
|
||||
)
|
||||
base_classes = get_base_classes(_class)
|
||||
# Adding function to base classes to allow
|
||||
# the output to be a function
|
||||
if add_function:
|
||||
base_classes.append("Callable")
|
||||
return {
|
||||
"template": format_dict(variables, name),
|
||||
"description": docs.short_description or "",
|
||||
"base_classes": base_classes,
|
||||
}
|
||||
|
||||
|
||||
def build_template_from_method(
|
||||
class_name: str,
|
||||
method_name: str,
|
||||
|
|
@ -462,3 +416,35 @@ def build_loader_repr_from_records(records: List[Record]) -> str:
|
|||
\nAvg. Record Length (characters): {int(avg_length)}
|
||||
Records: {records[:3]}..."""
|
||||
return "0 records"
|
||||
|
||||
|
||||
def update_settings(
|
||||
config: Optional[str] = None,
|
||||
cache: Optional[str] = None,
|
||||
dev: bool = False,
|
||||
remove_api_keys: bool = False,
|
||||
components_path: Optional[Path] = None,
|
||||
store: bool = True,
|
||||
):
|
||||
"""Update the settings from a config file."""
|
||||
from langflow.services.utils import initialize_settings_service
|
||||
|
||||
# Check for database_url in the environment variables
|
||||
|
||||
initialize_settings_service()
|
||||
settings_service = get_settings_service()
|
||||
if config:
|
||||
logger.debug(f"Loading settings from {config}")
|
||||
settings_service.settings.update_from_yaml(config, dev=dev)
|
||||
if remove_api_keys:
|
||||
logger.debug(f"Setting remove_api_keys to {remove_api_keys}")
|
||||
settings_service.settings.update_settings(REMOVE_API_KEYS=remove_api_keys)
|
||||
if cache:
|
||||
logger.debug(f"Setting cache to {cache}")
|
||||
settings_service.settings.update_settings(CACHE=cache)
|
||||
if components_path:
|
||||
logger.debug(f"Adding component path {components_path}")
|
||||
settings_service.settings.update_settings(COMPONENTS_PATH=components_path)
|
||||
if not store:
|
||||
logger.debug("Setting store to False")
|
||||
settings_service.settings.update_settings(STORE=False)
|
||||
|
|
|
|||
2108
src/backend/base/poetry.lock
generated
2108
src/backend/base/poetry.lock
generated
File diff suppressed because it is too large
Load diff
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