| @@ -28,28 +28,28 @@ from services.account_service import RegisterService, TenantService | |||
| @click.command("reset-password", help="Reset the account password.") | |||
| @click.option("--email", prompt=True, help="The email address of the account whose password you need to reset") | |||
| @click.option("--new-password", prompt=True, help="the new password.") | |||
| @click.option("--password-confirm", prompt=True, help="the new password confirm.") | |||
| @click.option("--email", prompt=True, help="Account email to reset password for") | |||
| @click.option("--new-password", prompt=True, help="New password") | |||
| @click.option("--password-confirm", prompt=True, help="Confirm new password") | |||
| def reset_password(email, new_password, password_confirm): | |||
| """ | |||
| Reset password of owner account | |||
| Only available in SELF_HOSTED mode | |||
| """ | |||
| if str(new_password).strip() != str(password_confirm).strip(): | |||
| click.echo(click.style("sorry. The two passwords do not match.", fg="red")) | |||
| click.echo(click.style("Passwords do not match.", fg="red")) | |||
| return | |||
| account = db.session.query(Account).filter(Account.email == email).one_or_none() | |||
| if not account: | |||
| click.echo(click.style("sorry. the account: [{}] not exist .".format(email), fg="red")) | |||
| click.echo(click.style("Account not found for email: {}".format(email), fg="red")) | |||
| return | |||
| try: | |||
| valid_password(new_password) | |||
| except: | |||
| click.echo(click.style("sorry. The passwords must match {} ".format(password_pattern), fg="red")) | |||
| click.echo(click.style("Invalid password. Must match {}".format(password_pattern), fg="red")) | |||
| return | |||
| # generate password salt | |||
| @@ -62,37 +62,37 @@ def reset_password(email, new_password, password_confirm): | |||
| account.password = base64_password_hashed | |||
| account.password_salt = base64_salt | |||
| db.session.commit() | |||
| click.echo(click.style("Congratulations! Password has been reset.", fg="green")) | |||
| click.echo(click.style("Password reset successfully.", fg="green")) | |||
| @click.command("reset-email", help="Reset the account email.") | |||
| @click.option("--email", prompt=True, help="The old email address of the account whose email you need to reset") | |||
| @click.option("--new-email", prompt=True, help="the new email.") | |||
| @click.option("--email-confirm", prompt=True, help="the new email confirm.") | |||
| @click.option("--email", prompt=True, help="Current account email") | |||
| @click.option("--new-email", prompt=True, help="New email") | |||
| @click.option("--email-confirm", prompt=True, help="Confirm new email") | |||
| def reset_email(email, new_email, email_confirm): | |||
| """ | |||
| Replace account email | |||
| :return: | |||
| """ | |||
| if str(new_email).strip() != str(email_confirm).strip(): | |||
| click.echo(click.style("Sorry, new email and confirm email do not match.", fg="red")) | |||
| click.echo(click.style("New emails do not match.", fg="red")) | |||
| return | |||
| account = db.session.query(Account).filter(Account.email == email).one_or_none() | |||
| if not account: | |||
| click.echo(click.style("sorry. the account: [{}] not exist .".format(email), fg="red")) | |||
| click.echo(click.style("Account not found for email: {}".format(email), fg="red")) | |||
| return | |||
| try: | |||
| email_validate(new_email) | |||
| except: | |||
| click.echo(click.style("sorry. {} is not a valid email. ".format(email), fg="red")) | |||
| click.echo(click.style("Invalid email: {}".format(new_email), fg="red")) | |||
| return | |||
| account.email = new_email | |||
| db.session.commit() | |||
| click.echo(click.style("Congratulations!, email has been reset.", fg="green")) | |||
| click.echo(click.style("Email updated successfully.", fg="green")) | |||
| @click.command( | |||
| @@ -104,7 +104,7 @@ def reset_email(email, new_email, email_confirm): | |||
| ) | |||
| @click.confirmation_option( | |||
| prompt=click.style( | |||
| "Are you sure you want to reset encrypt key pair? this operation cannot be rolled back!", fg="red" | |||
| "Are you sure you want to reset encrypt key pair? This operation cannot be rolled back!", fg="red" | |||
| ) | |||
| ) | |||
| def reset_encrypt_key_pair(): | |||
| @@ -114,13 +114,13 @@ def reset_encrypt_key_pair(): | |||
| Only support SELF_HOSTED mode. | |||
| """ | |||
| if dify_config.EDITION != "SELF_HOSTED": | |||
| click.echo(click.style("Sorry, only support SELF_HOSTED mode.", fg="red")) | |||
| click.echo(click.style("This command is only for SELF_HOSTED installations.", fg="red")) | |||
| return | |||
| tenants = db.session.query(Tenant).all() | |||
| for tenant in tenants: | |||
| if not tenant: | |||
| click.echo(click.style("Sorry, no workspace found. Please enter /install to initialize.", fg="red")) | |||
| click.echo(click.style("No workspaces found. Run /install first.", fg="red")) | |||
| return | |||
| tenant.encrypt_public_key = generate_key_pair(tenant.id) | |||
| @@ -137,7 +137,7 @@ def reset_encrypt_key_pair(): | |||
| ) | |||
| @click.command("vdb-migrate", help="migrate vector db.") | |||
| @click.command("vdb-migrate", help="Migrate vector db.") | |||
| @click.option("--scope", default="all", prompt=False, help="The scope of vector database to migrate, Default is All.") | |||
| def vdb_migrate(scope: str): | |||
| if scope in {"knowledge", "all"}: | |||
| @@ -150,7 +150,7 @@ def migrate_annotation_vector_database(): | |||
| """ | |||
| Migrate annotation datas to target vector database . | |||
| """ | |||
| click.echo(click.style("Start migrate annotation data.", fg="green")) | |||
| click.echo(click.style("Starting annotation data migration.", fg="green")) | |||
| create_count = 0 | |||
| skipped_count = 0 | |||
| total_count = 0 | |||
| @@ -174,14 +174,14 @@ def migrate_annotation_vector_database(): | |||
| f"Processing the {total_count} app {app.id}. " + f"{create_count} created, {skipped_count} skipped." | |||
| ) | |||
| try: | |||
| click.echo("Create app annotation index: {}".format(app.id)) | |||
| click.echo("Creating app annotation index: {}".format(app.id)) | |||
| app_annotation_setting = ( | |||
| db.session.query(AppAnnotationSetting).filter(AppAnnotationSetting.app_id == app.id).first() | |||
| ) | |||
| if not app_annotation_setting: | |||
| skipped_count = skipped_count + 1 | |||
| click.echo("App annotation setting is disabled: {}".format(app.id)) | |||
| click.echo("App annotation setting disabled: {}".format(app.id)) | |||
| continue | |||
| # get dataset_collection_binding info | |||
| dataset_collection_binding = ( | |||
| @@ -190,7 +190,7 @@ def migrate_annotation_vector_database(): | |||
| .first() | |||
| ) | |||
| if not dataset_collection_binding: | |||
| click.echo("App annotation collection binding is not exist: {}".format(app.id)) | |||
| click.echo("App annotation collection binding not found: {}".format(app.id)) | |||
| continue | |||
| annotations = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app.id).all() | |||
| dataset = Dataset( | |||
| @@ -211,11 +211,11 @@ def migrate_annotation_vector_database(): | |||
| documents.append(document) | |||
| vector = Vector(dataset, attributes=["doc_id", "annotation_id", "app_id"]) | |||
| click.echo(f"Start to migrate annotation, app_id: {app.id}.") | |||
| click.echo(f"Migrating annotations for app: {app.id}.") | |||
| try: | |||
| vector.delete() | |||
| click.echo(click.style(f"Successfully delete vector index for app: {app.id}.", fg="green")) | |||
| click.echo(click.style(f"Deleted vector index for app {app.id}.", fg="green")) | |||
| except Exception as e: | |||
| click.echo(click.style(f"Failed to delete vector index for app {app.id}.", fg="red")) | |||
| raise e | |||
| @@ -223,12 +223,12 @@ def migrate_annotation_vector_database(): | |||
| try: | |||
| click.echo( | |||
| click.style( | |||
| f"Start to created vector index with {len(documents)} annotations for app {app.id}.", | |||
| f"Creating vector index with {len(documents)} annotations for app {app.id}.", | |||
| fg="green", | |||
| ) | |||
| ) | |||
| vector.create(documents) | |||
| click.echo(click.style(f"Successfully created vector index for app {app.id}.", fg="green")) | |||
| click.echo(click.style(f"Created vector index for app {app.id}.", fg="green")) | |||
| except Exception as e: | |||
| click.echo(click.style(f"Failed to created vector index for app {app.id}.", fg="red")) | |||
| raise e | |||
| @@ -237,14 +237,14 @@ def migrate_annotation_vector_database(): | |||
| except Exception as e: | |||
| click.echo( | |||
| click.style( | |||
| "Create app annotation index error: {} {}".format(e.__class__.__name__, str(e)), fg="red" | |||
| "Error creating app annotation index: {} {}".format(e.__class__.__name__, str(e)), fg="red" | |||
| ) | |||
| ) | |||
| continue | |||
| click.echo( | |||
| click.style( | |||
| f"Congratulations! Create {create_count} app annotation indexes, and skipped {skipped_count} apps.", | |||
| f"Migration complete. Created {create_count} app annotation indexes. Skipped {skipped_count} apps.", | |||
| fg="green", | |||
| ) | |||
| ) | |||
| @@ -254,7 +254,7 @@ def migrate_knowledge_vector_database(): | |||
| """ | |||
| Migrate vector database datas to target vector database . | |||
| """ | |||
| click.echo(click.style("Start migrate vector db.", fg="green")) | |||
| click.echo(click.style("Starting vector database migration.", fg="green")) | |||
| create_count = 0 | |||
| skipped_count = 0 | |||
| total_count = 0 | |||
| @@ -278,7 +278,7 @@ def migrate_knowledge_vector_database(): | |||
| f"Processing the {total_count} dataset {dataset.id}. {create_count} created, {skipped_count} skipped." | |||
| ) | |||
| try: | |||
| click.echo("Create dataset vdb index: {}".format(dataset.id)) | |||
| click.echo("Creating dataset vector database index: {}".format(dataset.id)) | |||
| if dataset.index_struct_dict: | |||
| if dataset.index_struct_dict["type"] == vector_type: | |||
| skipped_count = skipped_count + 1 | |||
| @@ -299,7 +299,7 @@ def migrate_knowledge_vector_database(): | |||
| if dataset_collection_binding: | |||
| collection_name = dataset_collection_binding.collection_name | |||
| else: | |||
| raise ValueError("Dataset Collection Bindings is not exist!") | |||
| raise ValueError("Dataset Collection Binding not found") | |||
| else: | |||
| dataset_id = dataset.id | |||
| collection_name = Dataset.gen_collection_name_by_id(dataset_id) | |||
| @@ -351,14 +351,12 @@ def migrate_knowledge_vector_database(): | |||
| raise ValueError(f"Vector store {vector_type} is not supported.") | |||
| vector = Vector(dataset) | |||
| click.echo(f"Start to migrate dataset {dataset.id}.") | |||
| click.echo(f"Migrating dataset {dataset.id}.") | |||
| try: | |||
| vector.delete() | |||
| click.echo( | |||
| click.style( | |||
| f"Successfully delete vector index {collection_name} for dataset {dataset.id}.", fg="green" | |||
| ) | |||
| click.style(f"Deleted vector index {collection_name} for dataset {dataset.id}.", fg="green") | |||
| ) | |||
| except Exception as e: | |||
| click.echo( | |||
| @@ -410,15 +408,13 @@ def migrate_knowledge_vector_database(): | |||
| try: | |||
| click.echo( | |||
| click.style( | |||
| f"Start to created vector index with {len(documents)} documents of {segments_count}" | |||
| f"Creating vector index with {len(documents)} documents of {segments_count}" | |||
| f" segments for dataset {dataset.id}.", | |||
| fg="green", | |||
| ) | |||
| ) | |||
| vector.create(documents) | |||
| click.echo( | |||
| click.style(f"Successfully created vector index for dataset {dataset.id}.", fg="green") | |||
| ) | |||
| click.echo(click.style(f"Created vector index for dataset {dataset.id}.", fg="green")) | |||
| except Exception as e: | |||
| click.echo(click.style(f"Failed to created vector index for dataset {dataset.id}.", fg="red")) | |||
| raise e | |||
| @@ -429,13 +425,13 @@ def migrate_knowledge_vector_database(): | |||
| except Exception as e: | |||
| db.session.rollback() | |||
| click.echo( | |||
| click.style("Create dataset index error: {} {}".format(e.__class__.__name__, str(e)), fg="red") | |||
| click.style("Error creating dataset index: {} {}".format(e.__class__.__name__, str(e)), fg="red") | |||
| ) | |||
| continue | |||
| click.echo( | |||
| click.style( | |||
| f"Congratulations! Create {create_count} dataset indexes, and skipped {skipped_count} datasets.", fg="green" | |||
| f"Migration complete. Created {create_count} dataset indexes. Skipped {skipped_count} datasets.", fg="green" | |||
| ) | |||
| ) | |||
| @@ -445,7 +441,7 @@ def convert_to_agent_apps(): | |||
| """ | |||
| Convert Agent Assistant to Agent App. | |||
| """ | |||
| click.echo(click.style("Start convert to agent apps.", fg="green")) | |||
| click.echo(click.style("Starting convert to agent apps.", fg="green")) | |||
| proceeded_app_ids = [] | |||
| @@ -496,23 +492,23 @@ def convert_to_agent_apps(): | |||
| except Exception as e: | |||
| click.echo(click.style("Convert app error: {} {}".format(e.__class__.__name__, str(e)), fg="red")) | |||
| click.echo(click.style("Congratulations! Converted {} agent apps.".format(len(proceeded_app_ids)), fg="green")) | |||
| click.echo(click.style("Conversion complete. Converted {} agent apps.".format(len(proceeded_app_ids)), fg="green")) | |||
| @click.command("add-qdrant-doc-id-index", help="add qdrant doc_id index.") | |||
| @click.option("--field", default="metadata.doc_id", prompt=False, help="index field , default is metadata.doc_id.") | |||
| @click.command("add-qdrant-doc-id-index", help="Add Qdrant doc_id index.") | |||
| @click.option("--field", default="metadata.doc_id", prompt=False, help="Index field , default is metadata.doc_id.") | |||
| def add_qdrant_doc_id_index(field: str): | |||
| click.echo(click.style("Start add qdrant doc_id index.", fg="green")) | |||
| click.echo(click.style("Starting Qdrant doc_id index creation.", fg="green")) | |||
| vector_type = dify_config.VECTOR_STORE | |||
| if vector_type != "qdrant": | |||
| click.echo(click.style("Sorry, only support qdrant vector store.", fg="red")) | |||
| click.echo(click.style("This command only supports Qdrant vector store.", fg="red")) | |||
| return | |||
| create_count = 0 | |||
| try: | |||
| bindings = db.session.query(DatasetCollectionBinding).all() | |||
| if not bindings: | |||
| click.echo(click.style("Sorry, no dataset collection bindings found.", fg="red")) | |||
| click.echo(click.style("No dataset collection bindings found.", fg="red")) | |||
| return | |||
| import qdrant_client | |||
| from qdrant_client.http.exceptions import UnexpectedResponse | |||
| @@ -522,7 +518,7 @@ def add_qdrant_doc_id_index(field: str): | |||
| for binding in bindings: | |||
| if dify_config.QDRANT_URL is None: | |||
| raise ValueError("Qdrant url is required.") | |||
| raise ValueError("Qdrant URL is required.") | |||
| qdrant_config = QdrantConfig( | |||
| endpoint=dify_config.QDRANT_URL, | |||
| api_key=dify_config.QDRANT_API_KEY, | |||
| @@ -539,41 +535,39 @@ def add_qdrant_doc_id_index(field: str): | |||
| except UnexpectedResponse as e: | |||
| # Collection does not exist, so return | |||
| if e.status_code == 404: | |||
| click.echo( | |||
| click.style(f"Collection not found, collection_name:{binding.collection_name}.", fg="red") | |||
| ) | |||
| click.echo(click.style(f"Collection not found: {binding.collection_name}.", fg="red")) | |||
| continue | |||
| # Some other error occurred, so re-raise the exception | |||
| else: | |||
| click.echo( | |||
| click.style( | |||
| f"Failed to create qdrant index, collection_name:{binding.collection_name}.", fg="red" | |||
| f"Failed to create Qdrant index for collection: {binding.collection_name}.", fg="red" | |||
| ) | |||
| ) | |||
| except Exception as e: | |||
| click.echo(click.style("Failed to create qdrant client.", fg="red")) | |||
| click.echo(click.style("Failed to create Qdrant client.", fg="red")) | |||
| click.echo(click.style(f"Congratulations! Create {create_count} collection indexes.", fg="green")) | |||
| click.echo(click.style(f"Index creation complete. Created {create_count} collection indexes.", fg="green")) | |||
| @click.command("create-tenant", help="Create account and tenant.") | |||
| @click.option("--email", prompt=True, help="The email address of the tenant account.") | |||
| @click.option("--name", prompt=True, help="The workspace name of the tenant account.") | |||
| @click.option("--email", prompt=True, help="Tenant account email.") | |||
| @click.option("--name", prompt=True, help="Workspace name.") | |||
| @click.option("--language", prompt=True, help="Account language, default: en-US.") | |||
| def create_tenant(email: str, language: Optional[str] = None, name: Optional[str] = None): | |||
| """ | |||
| Create tenant account | |||
| """ | |||
| if not email: | |||
| click.echo(click.style("Sorry, email is required.", fg="red")) | |||
| click.echo(click.style("Email is required.", fg="red")) | |||
| return | |||
| # Create account | |||
| email = email.strip() | |||
| if "@" not in email: | |||
| click.echo(click.style("Sorry, invalid email address.", fg="red")) | |||
| click.echo(click.style("Invalid email address.", fg="red")) | |||
| return | |||
| account_name = email.split("@")[0] | |||
| @@ -593,19 +587,19 @@ def create_tenant(email: str, language: Optional[str] = None, name: Optional[str | |||
| click.echo( | |||
| click.style( | |||
| "Congratulations! Account and tenant created.\nAccount: {}\nPassword: {}".format(email, new_password), | |||
| "Account and tenant created.\nAccount: {}\nPassword: {}".format(email, new_password), | |||
| fg="green", | |||
| ) | |||
| ) | |||
| @click.command("upgrade-db", help="upgrade the database") | |||
| @click.command("upgrade-db", help="Upgrade the database") | |||
| def upgrade_db(): | |||
| click.echo("Preparing database migration...") | |||
| lock = redis_client.lock(name="db_upgrade_lock", timeout=60) | |||
| if lock.acquire(blocking=False): | |||
| try: | |||
| click.echo(click.style("Start database migration.", fg="green")) | |||
| click.echo(click.style("Starting database migration.", fg="green")) | |||
| # run db migration | |||
| import flask_migrate | |||
| @@ -615,7 +609,7 @@ def upgrade_db(): | |||
| click.echo(click.style("Database migration successful!", fg="green")) | |||
| except Exception as e: | |||
| logging.exception(f"Database migration failed, error: {e}") | |||
| logging.exception(f"Database migration failed: {e}") | |||
| finally: | |||
| lock.release() | |||
| else: | |||
| @@ -627,7 +621,7 @@ def fix_app_site_missing(): | |||
| """ | |||
| Fix app related site missing issue. | |||
| """ | |||
| click.echo(click.style("Start fix app related site missing issue.", fg="green")) | |||
| click.echo(click.style("Starting fix for missing app-related sites.", fg="green")) | |||
| failed_app_ids = [] | |||
| while True: | |||
| @@ -650,22 +644,22 @@ where sites.id is null limit 1000""" | |||
| if tenant: | |||
| accounts = tenant.get_accounts() | |||
| if not accounts: | |||
| print("Fix app {} failed.".format(app.id)) | |||
| print("Fix failed for app {}".format(app.id)) | |||
| continue | |||
| account = accounts[0] | |||
| print("Fix app {} related site missing issue.".format(app.id)) | |||
| print("Fixing missing site for app {}".format(app.id)) | |||
| app_was_created.send(app, account=account) | |||
| except Exception as e: | |||
| failed_app_ids.append(app_id) | |||
| click.echo(click.style("Fix app {} related site missing issue failed!".format(app_id), fg="red")) | |||
| click.echo(click.style("FFailed to fix missing site for app {}".format(app_id), fg="red")) | |||
| logging.exception(f"Fix app related site missing issue failed, error: {e}") | |||
| continue | |||
| if not processed_count: | |||
| break | |||
| click.echo(click.style("Congratulations! Fix app related site missing issue successful!", fg="green")) | |||
| click.echo(click.style("Fix for missing app-related sites completed successfully!", fg="green")) | |||
| def register_commands(app): | |||