Refactor error page layout and fixes migration scripts (#1485)

This pull request introduces a comprehensive refactor of our
application's error page layout. The primary goal of these changes is to
enhance user experience during error states by providing clearer, more
helpful information and improving the overall aesthetics of the error
page.

Fixes #1274
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-02-29 15:31:09 -03:00 • committed by GitHub
commit 4bec4dee4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 492 additions and 395 deletions

View file

@ -1,6 +0,0 @@
#!/bin/sh
added_files=$(git diff --name-only --cached --diff-filter=d)
make format
git add ${added_files}

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.6.8" version = "0.6.9"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
maintainers = [ maintainers = [

View file

@ -109,7 +109,11 @@ def version_callback(value: bool):
@app.callback() @app.callback()
def main_entry_point( def main_entry_point(
version: bool = typer.Option( version: bool = typer.Option(
None, "--version", callback=version_callback, is_eager=True, help="Show the version and exit." None,
"--version",
callback=version_callback,
is_eager=True,
help="Show the version and exit.",
), ),
): ):
""" """

View file

@ -63,7 +63,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# This is the path to the db in the root of the project. # This is the path to the db in the root of the project.
# When the user runs the Langflow the database url will # When the user runs the Langflow the database url will
# be set dinamically. # be set dinamically.
sqlalchemy.url = sqlite:///../../../langflow.db sqlalchemy.url = sqlite:///./langflow.db
[post_write_hooks] [post_write_hooks]
@ -98,7 +98,7 @@ handlers =
qualname = sqlalchemy.engine qualname = sqlalchemy.engine
[logger_alembic] [logger_alembic]
level = INFO level = DEBUG
handlers = handlers =
qualname = alembic qualname = alembic

View file

@ -1,10 +1,11 @@
import os
from logging.config import fileConfig from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context from alembic import context
from loguru import logger
from sqlalchemy import engine_from_config, pool
from langflow.services.database.models import * # noqa
from langflow.services.database.service import SQLModel from langflow.services.database.service import SQLModel
# this is the Alembic Config object, which provides # this is the Alembic Config object, which provides
@ -40,7 +41,8 @@ def run_migrations_offline() -> None:
script output. script output.
""" """
url = config.get_main_option("sqlalchemy.url") url = os.getenv("LANGFLOW_DATABASE_URL")
url = url or config.get_main_option("sqlalchemy.url")
context.configure( context.configure(
url=url, url=url,
target_metadata=target_metadata, target_metadata=target_metadata,
@ -60,12 +62,17 @@ def run_migrations_online() -> None:
and associate a connection with the context. and associate a connection with the context.
""" """
from langflow.services.deps import get_db_service
try:
connectable = get_db_service().engine
except Exception as e:
logger.error(f"Error getting database engine: {e}")
connectable = engine_from_config( connectable = engine_from_config(
config.get_section(config.config_ini_section, {}), config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.", prefix="sqlalchemy.",
poolclass=pool.NullPool, poolclass=pool.NullPool,
) )
with connectable.connect() as connection: with connectable.connect() as connection:
context.configure( context.configure(
connection=connection, target_metadata=target_metadata, render_as_batch=True connection=connection, target_metadata=target_metadata, render_as_batch=True

View file

@ -10,6 +10,7 @@ from typing import Sequence, Union
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel
from sqlalchemy.engine.reflection import Inspector
${imports if imports else ""} ${imports if imports else ""}
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@ -20,8 +21,12 @@ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None: def upgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
${upgrades if upgrades else "pass"} ${upgrades if upgrades else "pass"}
def downgrade() -> None: def downgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
${downgrades if downgrades else "pass"} ${downgrades if downgrades else "pass"}

View file

@ -5,28 +5,43 @@ Revises: 1ef9c4f3765d
Create Date: 2023-12-13 18:55:52.587360 Create Date: 2023-12-13 18:55:52.587360
""" """
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '006b3990db50' revision: str = "006b3990db50"
down_revision: Union[str, None] = '1ef9c4f3765d' down_revision: Union[str, None] = "1ef9c4f3765d"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
api_key_constraints = inspector.get_unique_constraints("apikey")
flow_constraints = inspector.get_unique_constraints("flow")
user_constraints = inspector.get_unique_constraints("user")
try: try:
with op.batch_alter_table('apikey', schema=None) as batch_op: if not any(
batch_op.create_unique_constraint('uq_apikey_id', ['id']) constraint["name"] == "uq_apikey_id" for constraint in api_key_constraints
):
with op.batch_alter_table("apikey", schema=None) as batch_op:
with op.batch_alter_table('flow', schema=None) as batch_op: batch_op.create_unique_constraint("uq_apikey_id", ["id"])
batch_op.create_unique_constraint('uq_flow_id', ['id']) if not any(
constraint["name"] == "uq_flow_id" for constraint in flow_constraints
with op.batch_alter_table('user', schema=None) as batch_op: ):
batch_op.create_unique_constraint('uq_user_id', ['id']) with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.create_unique_constraint("uq_flow_id", ["id"])
if not any(
constraint["name"] == "uq_user_id" for constraint in user_constraints
):
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.create_unique_constraint("uq_user_id", ["id"])
except Exception as e: except Exception as e:
print(e) print(e)
pass pass
@ -36,15 +51,24 @@ def upgrade() -> None:
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
api_key_constraints = inspector.get_unique_constraints("apikey")
flow_constraints = inspector.get_unique_constraints("flow")
user_constraints = inspector.get_unique_constraints("user")
try: try:
with op.batch_alter_table('user', schema=None) as batch_op: if any(
batch_op.drop_constraint('uq_user_id', type_='unique') constraint["name"] == "uq_apikey_id" for constraint in api_key_constraints
):
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.drop_constraint("uq_user_id", type_="unique")
if any(constraint["name"] == "uq_flow_id" for constraint in flow_constraints):
with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.drop_constraint("uq_flow_id", type_="unique")
if any(constraint["name"] == "uq_user_id" for constraint in user_constraints):
with op.batch_alter_table('flow', schema=None) as batch_op: with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.drop_constraint('uq_flow_id', type_='unique') batch_op.drop_constraint("uq_apikey_id", type_="unique")
with op.batch_alter_table('apikey', schema=None) as batch_op:
batch_op.drop_constraint('uq_apikey_id', type_='unique')
except Exception as e: except Exception as e:
print(e) print(e)
pass pass

View file

@ -5,67 +5,25 @@ Revises: 006b3990db50
Create Date: 2024-01-17 10:32:56.686287 Create Date: 2024-01-17 10:32:56.686287
""" """
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '0b8757876a7c' revision: str = "0b8757876a7c"
down_revision: Union[str, None] = '006b3990db50' down_revision: Union[str, None] = "006b3990db50"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try:
with op.batch_alter_table('apikey', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_apikey_api_key'), ['api_key'], unique=True)
batch_op.create_index(batch_op.f('ix_apikey_name'), ['name'], unique=False)
batch_op.create_index(batch_op.f('ix_apikey_user_id'), ['user_id'], unique=False)
except Exception as e:
print(e)
pass
try:
with op.batch_alter_table('flow', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_flow_description'), ['description'], unique=False)
batch_op.create_index(batch_op.f('ix_flow_name'), ['name'], unique=False)
batch_op.create_index(batch_op.f('ix_flow_user_id'), ['user_id'], unique=False)
except Exception as e:
print(e)
pass pass
try:
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_user_username'), ['username'], unique=True)
except Exception as e:
print(e)
pass
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try:
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_username'))
except Exception as e:
print(e)
pass
try:
with op.batch_alter_table('flow', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_flow_user_id'))
batch_op.drop_index(batch_op.f('ix_flow_name'))
batch_op.drop_index(batch_op.f('ix_flow_description'))
except Exception as e:
print(e)
pass
try:
with op.batch_alter_table('apikey', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_apikey_user_id'))
batch_op.drop_index(batch_op.f('ix_apikey_name'))
batch_op.drop_index(batch_op.f('ix_apikey_api_key'))
except Exception as e:
print(e)
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###

View file

@ -6,6 +6,7 @@ Revises: fd531f8868b1
Create Date: 2023-12-04 15:00:27.968998 Create Date: 2023-12-04 15:00:27.968998
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
@ -13,8 +14,8 @@ import sqlmodel
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '1ef9c4f3765d' revision: str = "1ef9c4f3765d"
down_revision: Union[str, None] = 'fd531f8868b1' down_revision: Union[str, None] = "fd531f8868b1"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
@ -22,10 +23,10 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try: try:
with op.batch_alter_table('apikey', schema=None) as batch_op: with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.alter_column('name', batch_op.alter_column(
existing_type=sqlmodel.sql.sqltypes.AutoString(), "name", existing_type=sqlmodel.sql.sqltypes.AutoString(), nullable=True
nullable=True) )
except Exception as e: except Exception as e:
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###
@ -34,10 +35,8 @@ def upgrade() -> None:
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try: try:
with op.batch_alter_table('apikey', schema=None) as batch_op: with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.alter_column('name', batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=False)
existing_type=sa.VARCHAR(),
nullable=False)
except Exception as e: except Exception as e:
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###

View file

@ -5,6 +5,7 @@ Revises:
Create Date: 2023-08-27 19:49:02.681355 Create Date: 2023-08-27 19:49:02.681355
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
@ -33,7 +34,9 @@ def upgrade() -> None:
if "ix_flowstyle_flow_id" in [ if "ix_flowstyle_flow_id" in [
index["name"] for index in inspector.get_indexes("flowstyle") index["name"] for index in inspector.get_indexes("flowstyle")
]: ]:
op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle") op.drop_index(
"ix_flowstyle_flow_id", table_name="flowstyle", if_exists=True
)
existing_indices_flow = [] existing_indices_flow = []
existing_fks_flow = [] existing_fks_flow = []
@ -80,8 +83,7 @@ def upgrade() -> None:
sa.Column("api_key", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column("api_key", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.ForeignKeyConstraint( sa.ForeignKeyConstraint(
["user_id"], ["user_id"], ["user.id"], name="fk_apikey_user_id_user"
["user.id"],
), ),
sa.PrimaryKeyConstraint("id", name="pk_apikey"), sa.PrimaryKeyConstraint("id", name="pk_apikey"),
sa.UniqueConstraint("id", name="uq_apikey_id"), sa.UniqueConstraint("id", name="uq_apikey_id"),
@ -103,8 +105,7 @@ def upgrade() -> None:
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.ForeignKeyConstraint( sa.ForeignKeyConstraint(
["user_id"], ["user_id"], ["user.id"], name="fk_flow_user_id_user"
["user.id"],
), ),
sa.PrimaryKeyConstraint("id", name="pk_flow"), sa.PrimaryKeyConstraint("id", name="pk_flow"),
sa.UniqueConstraint("id", name="uq_flow_id"), sa.UniqueConstraint("id", name="uq_flow_id"),
@ -151,21 +152,21 @@ def downgrade() -> None:
existing_tables = inspector.get_table_names() existing_tables = inspector.get_table_names()
if "flow" in existing_tables: if "flow" in existing_tables:
with op.batch_alter_table("flow", schema=None) as batch_op: with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_flow_user_id")) batch_op.drop_index(batch_op.f("ix_flow_user_id"), if_exists=True)
batch_op.drop_index(batch_op.f("ix_flow_name")) batch_op.drop_index(batch_op.f("ix_flow_name"), if_exists=True)
batch_op.drop_index(batch_op.f("ix_flow_description")) batch_op.drop_index(batch_op.f("ix_flow_description"), if_exists=True)
op.drop_table("flow") op.drop_table("flow")
if "apikey" in existing_tables: if "apikey" in existing_tables:
with op.batch_alter_table("apikey", schema=None) as batch_op: with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_apikey_user_id")) batch_op.drop_index(batch_op.f("ix_apikey_user_id"), if_exists=True)
batch_op.drop_index(batch_op.f("ix_apikey_name")) batch_op.drop_index(batch_op.f("ix_apikey_name"), if_exists=True)
batch_op.drop_index(batch_op.f("ix_apikey_api_key")) batch_op.drop_index(batch_op.f("ix_apikey_api_key"), if_exists=True)
op.drop_table("apikey") op.drop_table("apikey")
if "user" in existing_tables: if "user" in existing_tables:
with op.batch_alter_table("user", schema=None) as batch_op: with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.drop_index(batch_op.f("ix_user_username")) batch_op.drop_index(batch_op.f("ix_user_username"), if_exists=True)
op.drop_table("user") op.drop_table("user")

View file

@ -5,34 +5,44 @@ Revises: 7d2162acc8b2
Create Date: 2023-11-24 10:45:38.465302 Create Date: 2023-11-24 10:45:38.465302
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel
from alembic import op from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '2ac71eb9c3ae' revision: str = "2ac71eb9c3ae"
down_revision: Union[str, None] = '7d2162acc8b2' down_revision: Union[str, None] = "7d2162acc8b2"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
tables = inspector.get_table_names()
try: try:
op.create_table('credential', if "credential" not in tables:
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=True), op.create_table(
sa.Column('value', sqlmodel.sql.sqltypes.AutoString(), nullable=True), "credential",
sa.Column('provider', sqlmodel.sql.sqltypes.AutoString(), nullable=True), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column('user_id', sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column("value", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column('id', sqlmodel.sql.sqltypes.GUID(), nullable=False), sa.Column(
sa.Column('created_at', sa.DateTime(), nullable=False), "provider", sqlmodel.sql.sqltypes.AutoString(), nullable=True
sa.Column('updated_at', sa.DateTime(), nullable=True), ),
sa.PrimaryKeyConstraint('id'), sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("updated_at", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"),
) )
except Exception as e: except Exception as e:
print(e) print(e)
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###
@ -40,7 +50,7 @@ def upgrade() -> None:
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try: try:
op.drop_table('credential') op.drop_table("credential")
except Exception as e: except Exception as e:
print(e) print(e)
pass pass

View file

@ -5,6 +5,7 @@ Revises: 260dbcc8b680
Create Date: 2023-09-08 07:36:13.387318 Create Date: 2023-09-08 07:36:13.387318
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
@ -21,6 +22,7 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try:
conn = op.get_bind() conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore inspector = Inspector.from_engine(conn) # type: ignore
if "user" in inspector.get_table_names() and "profile_image" not in [ if "user" in inspector.get_table_names() and "profile_image" not in [
@ -29,15 +31,19 @@ def upgrade() -> None:
with op.batch_alter_table("user", schema=None) as batch_op: with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.add_column( batch_op.add_column(
sa.Column( sa.Column(
"profile_image", sqlmodel.sql.sqltypes.AutoString(), nullable=True "profile_image",
sqlmodel.sql.sqltypes.AutoString(),
nullable=True,
) )
) )
except Exception as e:
print(e)
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try:
conn = op.get_bind() conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore inspector = Inspector.from_engine(conn) # type: ignore
if "user" in inspector.get_table_names() and "profile_image" in [ if "user" in inspector.get_table_names() and "profile_image" in [
@ -45,5 +51,7 @@ def downgrade() -> None:
]: ]:
with op.batch_alter_table("user", schema=None) as batch_op: with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.drop_column("profile_image") batch_op.drop_column("profile_image")
except Exception as e:
print(e)
# ### end Alembic commands ### # ### end Alembic commands ###

View file

@ -5,12 +5,13 @@ Revises: eb5866d51fd2
Create Date: 2023-10-18 23:08:57.744906 Create Date: 2023-10-18 23:08:57.744906
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel
from alembic import op from alembic import op
from loguru import logger from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = "7843803a87b5" revision: str = "7843803a87b5"
@ -21,19 +22,26 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
flow_columns = [column["name"] for column in inspector.get_columns("flow")]
user_columns = [column["name"] for column in inspector.get_columns("user")]
try: try:
if "is_component" not in flow_columns:
with op.batch_alter_table("flow", schema=None) as batch_op: with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.add_column(sa.Column("is_component", sa.Boolean(), nullable=True))
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.add_column( batch_op.add_column(
sa.Column( sa.Column("is_component", sa.Boolean(), nullable=True)
"store_api_key", sqlmodel.AutoString(), nullable=True
)
) )
except Exception as e: except Exception as e:
logger.exception(e) pass
try:
if "store_api_key" not in user_columns:
with op.batch_alter_table("user", schema=None) as batch_op:
batch_op.add_column(
sa.Column("store_api_key", sqlmodel.AutoString(), nullable=True)
)
except Exception as e:
pass
# ### end Alembic commands ### # ### end Alembic commands ###

View file

@ -5,88 +5,74 @@ Revises: f5ee9749d1a6
Create Date: 2023-11-21 20:56:53.998781 Create Date: 2023-11-21 20:56:53.998781
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel
from alembic import op from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '7d2162acc8b2' revision: str = "7d2162acc8b2"
down_revision: Union[str, None] = 'f5ee9749d1a6' down_revision: Union[str, None] = "f5ee9749d1a6"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
api_key_columns = [column["name"] for column in inspector.get_columns("apikey")]
flow_columns = [column["name"] for column in inspector.get_columns("flow")]
try: try:
with op.batch_alter_table('component', schema=None) as batch_op: if "name" in api_key_columns:
batch_op.drop_index('ix_component_frontend_node_id') with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.drop_index('ix_component_name') batch_op.alter_column(
op.drop_table('component') "name", existing_type=sa.VARCHAR(), nullable=False
op.drop_table('flowstyle') )
except Exception as e: except Exception as e:
print(e) print(e)
pass
with op.batch_alter_table('apikey', schema=None) as batch_op:
batch_op.alter_column('name',
existing_type=sa.VARCHAR(),
nullable=False)
with op.batch_alter_table('flow', schema=None) as batch_op: pass
batch_op.add_column(sa.Column('updated_at', sa.DateTime(), nullable=True)) try:
batch_op.add_column(sa.Column('folder', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) with op.batch_alter_table("flow", schema=None) as batch_op:
if "updated_at" not in flow_columns:
batch_op.add_column(
sa.Column("updated_at", sa.DateTime(), nullable=True)
)
if "folder" not in flow_columns:
batch_op.add_column(
sa.Column(
"folder", sqlmodel.sql.sqltypes.AutoString(), nullable=True
)
)
except Exception as e:
print(e)
pass
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try: try:
with op.batch_alter_table('flow', schema=None) as batch_op: with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.drop_column('folder') batch_op.drop_column("folder")
batch_op.drop_column('updated_at') batch_op.drop_column("updated_at")
except Exception as e: except Exception as e:
print(e) print(e)
pass pass
try: try:
with op.batch_alter_table('apikey', schema=None) as batch_op: with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.alter_column('name', batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=True)
existing_type=sa.VARCHAR(),
nullable=True)
except Exception as e: except Exception as e:
print(e) print(e)
pass pass
try:
op.create_table('flowstyle',
sa.Column('color', sa.VARCHAR(), nullable=False),
sa.Column('emoji', sa.VARCHAR(), nullable=False),
sa.Column('flow_id', sa.CHAR(length=32), nullable=True),
sa.Column('id', sa.CHAR(length=32), nullable=False),
sa.ForeignKeyConstraint(['flow_id'], ['flow.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
op.create_table('component',
sa.Column('id', sa.CHAR(length=32), nullable=False),
sa.Column('frontend_node_id', sa.CHAR(length=32), nullable=False),
sa.Column('name', sa.VARCHAR(), nullable=False),
sa.Column('description', sa.VARCHAR(), nullable=True),
sa.Column('python_code', sa.VARCHAR(), nullable=True),
sa.Column('return_type', sa.VARCHAR(), nullable=True),
sa.Column('is_disabled', sa.BOOLEAN(), nullable=False),
sa.Column('is_read_only', sa.BOOLEAN(), nullable=False),
sa.Column('create_at', sa.DATETIME(), nullable=False),
sa.Column('update_at', sa.DATETIME(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('component', schema=None) as batch_op:
batch_op.create_index('ix_component_name', ['name'], unique=False)
batch_op.create_index('ix_component_frontend_node_id', ['frontend_node_id'], unique=False)
except Exception as e:
print(e)
pass
# ### end Alembic commands ### # ### end Alembic commands ###

View file

@ -5,55 +5,105 @@ Revises: 0b8757876a7c
Create Date: 2024-01-26 13:31:14.797548 Create Date: 2024-01-26 13:31:14.797548
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel
from alembic import op from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = 'b2fa308044b5' revision: str = "b2fa308044b5"
down_revision: Union[str, None] = '0b8757876a7c' down_revision: Union[str, None] = "0b8757876a7c"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
tables = inspector.get_table_names()
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try: try:
op.drop_table('flowstyle') if "flowstyle" in tables:
with op.batch_alter_table('flow', schema=None) as batch_op: op.drop_table("flowstyle")
batch_op.add_column(sa.Column('is_component', sa.Boolean(), nullable=True)) with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.add_column(sa.Column('updated_at', sa.DateTime(), nullable=True)) flow_columns = [column["name"] for column in inspector.get_columns("flow")]
batch_op.add_column(sa.Column('folder', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) if "is_component" not in flow_columns:
batch_op.add_column(sa.Column('user_id', sqlmodel.sql.sqltypes.GUID(), nullable=True)) batch_op.add_column(
batch_op.create_index(batch_op.f('ix_flow_user_id'), ['user_id'], unique=False) sa.Column("is_component", sa.Boolean(), nullable=True)
batch_op.create_foreign_key('fk_flow_user_id_user', 'user', ['user_id'], ['id']) )
if "updated_at" not in flow_columns:
batch_op.add_column(
sa.Column("updated_at", sa.DateTime(), nullable=True)
)
if "folder" not in flow_columns:
batch_op.add_column(
sa.Column(
"folder", sqlmodel.sql.sqltypes.AutoString(), nullable=True
)
)
if "user_id" not in flow_columns:
batch_op.add_column(
sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=True)
)
indices = inspector.get_indexes("flow")
indices_names = [index["name"] for index in indices]
if "ix_flow_user_id" not in indices_names:
batch_op.create_index(
batch_op.f("ix_flow_user_id"), ["user_id"], unique=False
)
if "fk_flow_user_id_user" not in indices_names:
batch_op.create_foreign_key(
"fk_flow_user_id_user", "user", ["user_id"], ["id"]
)
except Exception: except Exception:
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
try: try:
with op.batch_alter_table('flow', schema=None) as batch_op: # Re-create the dropped table 'flowstyle' if it was previously dropped in upgrade
batch_op.drop_constraint('fk_flow_user_id_user', type_='foreignkey') if "flowstyle" not in inspector.get_table_names():
batch_op.drop_index(batch_op.f('ix_flow_user_id')) op.create_table(
batch_op.drop_column('user_id') "flowstyle",
batch_op.drop_column('folder') sa.Column("color", sa.String(), nullable=False),
batch_op.drop_column('updated_at') sa.Column("emoji", sa.String(), nullable=False),
batch_op.drop_column('is_component') sa.Column("flow_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
op.create_table('flowstyle', sa.ForeignKeyConstraint(["flow_id"], ["flow.id"]),
sa.Column('color', sa.VARCHAR(), nullable=False), sa.PrimaryKeyConstraint("id"),
sa.Column('emoji', sa.VARCHAR(), nullable=False), sa.UniqueConstraint("id"),
sa.Column('flow_id', sa.CHAR(length=32), nullable=True),
sa.Column('id', sa.CHAR(length=32), nullable=False),
sa.ForeignKeyConstraint(['flow_id'], ['flow.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
) )
except Exception:
pass with op.batch_alter_table("flow", schema=None) as batch_op:
# ### end Alembic commands ### # Check and remove newly added columns and constraints in upgrade
flow_columns = [column["name"] for column in inspector.get_columns("flow")]
if "user_id" in flow_columns:
batch_op.drop_column("user_id")
if "folder" in flow_columns:
batch_op.drop_column("folder")
if "updated_at" in flow_columns:
batch_op.drop_column("updated_at")
if "is_component" in flow_columns:
batch_op.drop_column("is_component")
indices = inspector.get_indexes("flow")
indices_names = [index["name"] for index in indices]
if "ix_flow_user_id" in indices_names:
batch_op.drop_index("ix_flow_user_id")
# Assuming fk_flow_user_id_user is a foreign key constraint's name, not an index
constraints = inspector.get_foreign_keys("flow")
constraint_names = [constraint["name"] for constraint in constraints]
if "fk_flow_user_id_user" in constraint_names:
batch_op.drop_constraint("fk_flow_user_id_user", type_="foreignkey")
except Exception as e:
# It's generally a good idea to log the exception or handle it in a way other than a bare pass
print(f"Error during downgrade: {e}")

View file

@ -5,46 +5,68 @@ Revises: b2fa308044b5
Create Date: 2024-01-26 13:34:14.496769 Create Date: 2024-01-26 13:34:14.496769
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa
import sqlmodel import sqlmodel
from alembic import op from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = 'bc2f01c40e4a' revision: str = "bc2f01c40e4a"
down_revision: Union[str, None] = 'b2fa308044b5' down_revision: Union[str, None] = "b2fa308044b5"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### conn = op.get_bind()
try: inspector = Inspector.from_engine(conn) # type: ignore
with op.batch_alter_table('flow', schema=None) as batch_op: flow_columns = {column["name"] for column in inspector.get_columns("flow")}
batch_op.add_column(sa.Column('is_component', sa.Boolean(), nullable=True)) flow_indexes = {index["name"] for index in inspector.get_indexes("flow")}
batch_op.add_column(sa.Column('updated_at', sa.DateTime(), nullable=True)) flow_fks = {fk["name"] for fk in inspector.get_foreign_keys("flow")}
batch_op.add_column(sa.Column('folder', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
batch_op.add_column(sa.Column('user_id', sqlmodel.sql.sqltypes.GUID(), nullable=True)) with op.batch_alter_table("flow", schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_flow_user_id'), ['user_id'], unique=False) if "is_component" not in flow_columns:
batch_op.create_foreign_key('flow_user_id_fkey' batch_op.add_column(sa.Column("is_component", sa.Boolean(), nullable=True))
, 'user', ['user_id'], ['id']) if "updated_at" not in flow_columns:
except Exception: batch_op.add_column(sa.Column("updated_at", sa.DateTime(), nullable=True))
pass if "folder" not in flow_columns:
# ### end Alembic commands ### batch_op.add_column(
sa.Column("folder", sqlmodel.sql.sqltypes.AutoString(), nullable=True)
)
if "user_id" not in flow_columns:
batch_op.add_column(
sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=True)
)
if "ix_flow_user_id" not in flow_indexes:
batch_op.create_index(
batch_op.f("ix_flow_user_id"), ["user_id"], unique=False
)
if "flow_user_id_fkey" not in flow_fks:
batch_op.create_foreign_key(
"flow_user_id_fkey", "user", ["user_id"], ["id"]
)
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### conn = op.get_bind()
try: inspector = Inspector.from_engine(conn) # type: ignore
with op.batch_alter_table('flow', schema=None) as batch_op: flow_columns = {column["name"] for column in inspector.get_columns("flow")}
batch_op.drop_constraint('flow_user_id_fkey', type_='foreignkey') flow_indexes = {index["name"] for index in inspector.get_indexes("flow")}
batch_op.drop_index(batch_op.f('ix_flow_user_id')) flow_fks = {fk["name"] for fk in inspector.get_foreign_keys("flow")}
batch_op.drop_column('user_id')
batch_op.drop_column('folder')
batch_op.drop_column('updated_at')
batch_op.drop_column('is_component')
except Exception:
pass
# ### end Alembic commands ### with op.batch_alter_table("flow", schema=None) as batch_op:
if "flow_user_id_fkey" in flow_fks:
batch_op.drop_constraint("flow_user_id_fkey", type_="foreignkey")
if "ix_flow_user_id" in flow_indexes:
batch_op.drop_index(batch_op.f("ix_flow_user_id"))
if "user_id" in flow_columns:
batch_op.drop_column("user_id")
if "folder" in flow_columns:
batch_op.drop_column("folder")
if "updated_at" in flow_columns:
batch_op.drop_column("updated_at")
if "is_component" in flow_columns:
batch_op.drop_column("is_component")

View file

@ -5,11 +5,10 @@ Revises: 67cc006d50bf
Create Date: 2023-10-04 10:18:25.640458 Create Date: 2023-10-04 10:18:25.640458
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy import exc
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = "eb5866d51fd2" revision: str = "eb5866d51fd2"
@ -21,70 +20,12 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
connection = op.get_bind() connection = op.get_bind()
try:
op.drop_table("flowstyle")
with op.batch_alter_table("component", schema=None) as batch_op:
batch_op.drop_index("ix_component_frontend_node_id")
batch_op.drop_index("ix_component_name")
except exc.SQLAlchemyError:
# connection.execute(text("ROLLBACK"))
pass
except Exception as e:
print(e)
pass
try:
op.drop_table("component")
except exc.SQLAlchemyError:
# connection.execute(text("ROLLBACK"))
pass
except Exception as e:
print(e)
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
try:
op.create_table(
"component",
sa.Column("id", sa.CHAR(length=32), nullable=False),
sa.Column("frontend_node_id", sa.CHAR(length=32), nullable=False),
sa.Column("name", sa.VARCHAR(), nullable=False),
sa.Column("description", sa.VARCHAR(), nullable=True),
sa.Column("python_code", sa.VARCHAR(), nullable=True),
sa.Column("return_type", sa.VARCHAR(), nullable=True),
sa.Column("is_disabled", sa.BOOLEAN(), nullable=False),
sa.Column("is_read_only", sa.BOOLEAN(), nullable=False),
sa.Column("create_at", sa.DATETIME(), nullable=False),
sa.Column("update_at", sa.DATETIME(), nullable=False),
sa.PrimaryKeyConstraint("id", name="pk_component"),
)
with op.batch_alter_table("component", schema=None) as batch_op:
batch_op.create_index("ix_component_name", ["name"], unique=False)
batch_op.create_index(
"ix_component_frontend_node_id", ["frontend_node_id"], unique=False
)
except Exception as e:
print(e)
pass
try:
op.create_table(
"flowstyle",
sa.Column("color", sa.VARCHAR(), nullable=False),
sa.Column("emoji", sa.VARCHAR(), nullable=False),
sa.Column("flow_id", sa.CHAR(length=32), nullable=True),
sa.Column("id", sa.CHAR(length=32), nullable=False),
sa.ForeignKeyConstraint(
["flow_id"],
["flow.id"],
),
sa.PrimaryKeyConstraint("id", name="pk_flowstyle"),
sa.UniqueConstraint("id", name="uq_flowstyle_id"),
)
except Exception as e:
print(e)
pass pass
# ### end Alembic commands ### # ### end Alembic commands ###

View file

@ -5,6 +5,7 @@ Revises: 7843803a87b5
Create Date: 2023-10-18 23:12:27.297016 Create Date: 2023-10-18 23:12:27.297016
""" """
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa import sqlalchemy as sa

View file

@ -5,22 +5,35 @@ Revises: 2ac71eb9c3ae
Create Date: 2023-11-24 15:07:37.566516 Create Date: 2023-11-24 15:07:37.566516
""" """
from typing import Sequence, Union
from typing import Optional, Sequence, Union
from alembic import op from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = 'fd531f8868b1' revision: str = "fd531f8868b1"
down_revision: Union[str, None] = '2ac71eb9c3ae' down_revision: Union[str, None] = "2ac71eb9c3ae"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
tables = inspector.get_table_names()
foreign_keys_names = []
if "credential" in tables:
foreign_keys = inspector.get_foreign_keys("credential")
foreign_keys_names = [fk["name"] for fk in foreign_keys]
try: try:
with op.batch_alter_table('credential', schema=None) as batch_op: if "credential" in tables and "fk_credential_user_id" not in foreign_keys_names:
batch_op.create_foreign_key("fk_credential_user_id", 'user', ['user_id'], ['id']) with op.batch_alter_table("credential", schema=None) as batch_op:
batch_op.create_foreign_key(
"fk_credential_user_id", "user", ["user_id"], ["id"]
)
except Exception as e: except Exception as e:
print(e) print(e)
pass pass
@ -30,9 +43,17 @@ def upgrade() -> None:
def downgrade() -> None: def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
tables = inspector.get_table_names()
foreign_keys_names: list[Optional[str]] = []
if "credential" in tables:
foreign_keys = inspector.get_foreign_keys("credential")
foreign_keys_names = [fk["name"] for fk in foreign_keys]
try: try:
with op.batch_alter_table('credential', schema=None) as batch_op: if "credential" in tables and "fk_credential_user_id" in foreign_keys_names:
batch_op.drop_constraint("fk_credential_user_id", type_='foreignkey') with op.batch_alter_table("credential", schema=None) as batch_op:
batch_op.drop_constraint("fk_credential_user_id", type_="foreignkey")
except Exception as e: except Exception as e:
print(e) print(e)
pass pass

View file

@ -1,17 +1,28 @@
import time import time
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketException, status from fastapi import (
APIRouter,
Depends,
HTTPException,
WebSocket,
WebSocketException,
status,
)
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from loguru import logger
from sqlmodel import Session
from langflow.api.utils import build_input_keys_response, format_elapsed_time from langflow.api.utils import build_input_keys_response, format_elapsed_time
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
from langflow.graph.graph.base import Graph from langflow.graph.graph.base import Graph
from langflow.services.auth.utils import get_current_active_user, get_current_user_for_websocket from langflow.services.auth.utils import (
get_current_active_user,
get_current_user_for_websocket,
)
from langflow.services.cache.service import BaseCacheService from langflow.services.cache.service import BaseCacheService
from langflow.services.cache.utils import update_build_status from langflow.services.cache.utils import update_build_status
from langflow.services.chat.service import ChatService from langflow.services.chat.service import ChatService
from langflow.services.deps import get_cache_service, get_chat_service, get_session from langflow.services.deps import get_cache_service, get_chat_service, get_session
from loguru import logger
from sqlmodel import Session
router = APIRouter(tags=["Chat"]) router = APIRouter(tags=["Chat"])

View file

@ -5,16 +5,17 @@ from typing import TYPE_CHECKING
import sqlalchemy as sa import sqlalchemy as sa
from alembic import command, util from alembic import command, util
from alembic.config import Config 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.base import Service
from langflow.services.database import models # noqa from langflow.services.database import models # noqa
from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults from langflow.services.database.utils import Result, TableResults
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
from langflow.services.utils import teardown_superuser 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: if TYPE_CHECKING:
from sqlalchemy.engine import Engine from sqlalchemy.engine import Engine
@ -110,13 +111,11 @@ class DatabaseService(Service):
return True return True
def init_alembic(self): def init_alembic(self, alembic_cfg):
logger.info("Initializing alembic") logger.info("Initializing alembic")
alembic_cfg = Config() command.ensure_version(alembic_cfg)
alembic_cfg.set_main_option("script_location", str(self.script_location)) # alembic_cfg.attributes["connection"].commit()
alembic_cfg.set_main_option("sqlalchemy.url", self.database_url) command.upgrade(alembic_cfg, "head")
command.stamp(alembic_cfg, "head")
# command.upgrade(alembic_cfg, "head")
logger.info("Alembic initialized") logger.info("Alembic initialized")
def run_migrations(self, fix=False): def run_migrations(self, fix=False):
@ -125,6 +124,11 @@ class DatabaseService(Service):
# if not self.script_location.exists(): # this is not the correct way to check if alembic has been initialized # if not self.script_location.exists(): # this is not the correct way to check if alembic has been initialized
# We need to check if the alembic_version table exists # We need to check if the alembic_version table exists
# if not, we need to initialize alembic # if not, we need to initialize alembic
alembic_cfg = Config()
# alembic_cfg.attributes["connection"] = session
alembic_cfg.set_main_option("script_location", str(self.script_location))
alembic_cfg.set_main_option("sqlalchemy.url", self.database_url)
should_initialize_alembic = False
with Session(self.engine) as session: with Session(self.engine) as session:
# If the table does not exist it throws an error # If the table does not exist it throws an error
# so we need to catch it # so we need to catch it
@ -132,18 +136,19 @@ class DatabaseService(Service):
session.exec(text("SELECT * FROM alembic_version")) session.exec(text("SELECT * FROM alembic_version"))
except Exception: except Exception:
logger.info("Alembic not initialized") logger.info("Alembic not initialized")
should_initialize_alembic = True
else:
logger.info("Alembic already initialized")
if should_initialize_alembic:
try: try:
self.init_alembic() self.init_alembic(alembic_cfg)
except Exception as exc: except Exception as exc:
logger.error(f"Error initializing alembic: {exc}") logger.error(f"Error initializing alembic: {exc}")
raise RuntimeError("Error initializing alembic") from exc raise RuntimeError("Error initializing alembic") from exc
else:
logger.info("Alembic already initialized")
logger.info(f"Running DB migrations in {self.script_location}") logger.info(f"Running DB migrations in {self.script_location}")
alembic_cfg = Config()
alembic_cfg.set_main_option("script_location", str(self.script_location))
alembic_cfg.set_main_option("sqlalchemy.url", self.database_url)
try: try:
command.check(alembic_cfg) command.check(alembic_cfg)
except Exception as exc: except Exception as exc:
@ -154,7 +159,7 @@ class DatabaseService(Service):
try: try:
command.check(alembic_cfg) command.check(alembic_cfg)
except util.exc.AutogenerateDiffsDetected as e: except util.exc.AutogenerateDiffsDetected as e:
logger.exception("AutogenerateDiffsDetected: {exc}") logger.error("AutogenerateDiffsDetected: {exc}")
if not fix: if not fix:
raise RuntimeError( raise RuntimeError(
"Something went wrong running migrations. Please, run `langflow migration --fix`" "Something went wrong running migrations. Please, run `langflow migration --fix`"

View file

@ -1,10 +1,14 @@
from loguru import logger
from sqlmodel import Session, select
from langflow.services.auth.utils import create_super_user, verify_password from langflow.services.auth.utils import create_super_user, verify_password
from langflow.services.database.utils import initialize_database from langflow.services.database.utils import initialize_database
from langflow.services.manager import service_manager from langflow.services.manager import service_manager
from langflow.services.schema import ServiceType from langflow.services.schema import ServiceType
from langflow.services.settings.constants import DEFAULT_SUPERUSER, DEFAULT_SUPERUSER_PASSWORD from langflow.services.settings.constants import (
from loguru import logger DEFAULT_SUPERUSER,
from sqlmodel import Session, select DEFAULT_SUPERUSER_PASSWORD,
)
from .deps import get_db_service, get_session, get_settings_service from .deps import get_db_service, get_session, get_settings_service
@ -16,7 +20,9 @@ def get_factories_and_deps():
from langflow.services.credentials import factory as credentials_factory from langflow.services.credentials import factory as credentials_factory
from langflow.services.database import factory as database_factory from langflow.services.database import factory as database_factory
from langflow.services.plugins import factory as plugins_factory from langflow.services.plugins import factory as plugins_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
from langflow.services.settings import factory as settings_factory from langflow.services.settings import factory as settings_factory
from langflow.services.store import factory as store_factory from langflow.services.store import factory as store_factory
from langflow.services.task import factory as task_factory from langflow.services.task import factory as task_factory
@ -43,7 +49,10 @@ def get_factories_and_deps():
), ),
(plugins_factory.PluginServiceFactory(), [ServiceType.SETTINGS_SERVICE]), (plugins_factory.PluginServiceFactory(), [ServiceType.SETTINGS_SERVICE]),
(store_factory.StoreServiceFactory(), [ServiceType.SETTINGS_SERVICE]), (store_factory.StoreServiceFactory(), [ServiceType.SETTINGS_SERVICE]),
(credentials_factory.CredentialServiceFactory(), [ServiceType.SETTINGS_SERVICE]), (
credentials_factory.CredentialServiceFactory(),
[ServiceType.SETTINGS_SERVICE],
),
] ]
@ -173,7 +182,9 @@ def initialize_session_service():
Initialize the session manager. Initialize the session manager.
""" """
from langflow.services.cache import factory as cache_factory 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() initialize_settings_service()
@ -202,7 +213,7 @@ def initialize_services(fix_migration: bool = False):
try: try:
initialize_database(fix_migration=fix_migration) initialize_database(fix_migration=fix_migration)
except Exception as exc: except Exception as exc:
logger.exception(exc) logger.error(exc)
raise exc raise exc
setup_superuser(service_manager.get(ServiceType.SETTINGS_SERVICE), next(get_session())) setup_superuser(service_manager.get(ServiceType.SETTINGS_SERVICE), next(get_session()))
try: try:

View file

@ -20,8 +20,8 @@ import Router from "./routes";
import useAlertStore from "./stores/alertStore"; import useAlertStore from "./stores/alertStore";
import { useDarkStore } from "./stores/darkStore"; import { useDarkStore } from "./stores/darkStore";
import useFlowsManagerStore from "./stores/flowsManagerStore"; import useFlowsManagerStore from "./stores/flowsManagerStore";
import { useTypesStore } from "./stores/typesStore";
import { useStoreStore } from "./stores/storeStore"; import { useStoreStore } from "./stores/storeStore";
import { useTypesStore } from "./stores/typesStore";
export default function App() { export default function App() {
const errorData = useAlertStore((state) => state.errorData); const errorData = useAlertStore((state) => state.errorData);

View file

@ -1,37 +1,64 @@
import { XCircle } from "lucide-react";
import { crashComponentPropsType } from "../../types/components"; import { crashComponentPropsType } from "../../types/components";
import { Button } from "../ui/button";
import { Card, CardContent, CardFooter, CardHeader } from "../ui/card";
export default function CrashErrorComponent({ export default function CrashErrorComponent({
error, error,
resetErrorBoundary, resetErrorBoundary,
}: crashComponentPropsType): JSX.Element { }: crashComponentPropsType): JSX.Element {
return ( return (
<div className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-foreground bg-opacity-50"> <div className="z-50 flex h-screen w-screen items-center justify-center bg-foreground bg-opacity-50">
<div className="flex h-1/3 min-h-fit max-w-4xl flex-col justify-evenly rounded-lg bg-background p-8 text-start shadow-lg"> <div className="flex h-screen w-screen flex-col bg-background text-start shadow-lg">
<h1 className="mb-4 text-3xl text-status-red"> <div className="m-auto grid w-1/2 justify-center gap-5 text-center">
Oops! An unknown error has occurred. <Card className="p-8">
</h1> <CardHeader>
<div className="m-auto">
<XCircle strokeWidth={1.5} className="h-16 w-16" />
</div>
<div>
<p className="mb-4 text-xl text-foreground"> <p className="mb-4 text-xl text-foreground">
Please click the 'Reset Application' button to restore the Sorry, we found an unexpected error!
application's state. If the error persists, please create an issue on
our GitHub page. We apologize for any inconvenience this may have
caused.
</p> </p>
<div className="flex justify-center"> </div>
<button </CardHeader>
onClick={resetErrorBoundary}
className="mr-4 rounded bg-primary px-4 py-2 font-bold text-background hover:bg-ring" <CardContent className="grid">
<div>
<p>
Please report errors with detailed tracebacks on the{" "}
<a
href="https://github.com/logspace-ai/langflow/issues"
target="_blank"
rel="noopener noreferrer"
className="font-medium hover:underline "
> >
Reset Application GitHub Issues
</button> </a>{" "}
page.
<br></br>
Thank you!
</p>
</div>
</CardContent>
<CardFooter>
<div className="m-auto mt-4 flex justify-center">
<Button onClick={resetErrorBoundary}>Restart Langflow</Button>
<a <a
href="https://github.com/logspace-ai/langflow/issues/new" href="https://github.com/logspace-ai/langflow/issues/new"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="rounded bg-status-red px-4 py-2 font-bold text-background hover:bg-error-foreground"
> >
Create Issue <Button className="ml-3" variant={"outline"}>
Report on GitHub
</Button>
</a> </a>
</div> </div>
</CardFooter>
</Card>
</div>
</div> </div>
</div> </div>
); );

View file

@ -173,15 +173,25 @@ export default function NodeToolbarComponent({
</button> </button>
</ShadTooltip> </ShadTooltip>
{hasStore && ( {hasStore && (
<ShadTooltip content={(!hasApiKey || !validApiKey) ? "Set a valid API key to share this component." : "Share"} side="top"> <ShadTooltip
content={
!hasApiKey || !validApiKey
? "Set a valid API key to share this component."
: "Share"
}
side="top"
>
<button <button
className={classNames( className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10", "relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
(!hasApiKey || !validApiKey) ? " text-muted-foreground cursor-not-allowed" : "" !hasApiKey || !validApiKey
? " cursor-not-allowed text-muted-foreground"
: ""
)} )}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
if (hasApiKey && hasStore && validApiKey) setShowconfirmShare(true); if (hasApiKey && hasStore && validApiKey)
setShowconfirmShare(true);
}} }}
> >
<IconComponent name="Share3" className="-m-1 h-6 w-6" /> <IconComponent name="Share3" className="-m-1 h-6 w-6" />

View file

@ -21,11 +21,7 @@ import {
SelectValue, SelectValue,
} from "../../components/ui/select"; } from "../../components/ui/select";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { import { getStoreComponents, getStoreTags } from "../../controllers/API";
checkHasApiKey,
getStoreComponents,
getStoreTags,
} from "../../controllers/API";
import StoreApiKeyModal from "../../modals/StoreApiKeyModal"; import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -46,9 +42,7 @@ export default function StorePage(): JSX.Element {
const setCurrentFlowId = useFlowsManagerStore( const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId (state) => state.setCurrentFlowId
); );
const currentFlowId = useFlowsManagerStore( const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
(state) => state.currentFlowId
);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loadingTags, setLoadingTags] = useState(true); const [loadingTags, setLoadingTags] = useState(true);
const { id } = useParams(); const { id } = useParams();

View file

@ -1,7 +1,6 @@
import { create } from "zustand"; import { create } from "zustand";
import { checkHasApiKey, checkHasStore } from "../controllers/API"; import { checkHasApiKey, checkHasStore } from "../controllers/API";
import { StoreStoreType } from "../types/zustand/store"; import { StoreStoreType } from "../types/zustand/store";
import useAlertStore from "./alertStore";
export const useStoreStore = create<StoreStoreType>((set) => ({ export const useStoreStore = create<StoreStoreType>((set) => ({
hasStore: true, hasStore: true,

View file

@ -4,6 +4,7 @@ from collections import namedtuple
import pytest import pytest
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from langflow.interface.tools.constants import CUSTOM_TOOLS from langflow.interface.tools.constants import CUSTOM_TOOLS
from langflow.processing.process import Result from langflow.processing.process import Result
from langflow.services.auth.utils import get_password_hash from langflow.services.auth.utils import get_password_hash