| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  | import base64 | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  | import secrets | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import click | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  | from core.embedding.cached_embedding import CacheEmbedding | 
					
						
							| 
									
										
										
										
											2024-01-02 23:42:00 +08:00
										 |  |  | from core.model_manager import ModelManager | 
					
						
							|  |  |  | from core.model_runtime.entities.model_entities import ModelType | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | from extensions.ext_database import db | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | from flask import current_app | 
					
						
							| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  | from libs.helper import email as email_validate | 
					
						
							|  |  |  | from libs.password import hash_password, password_pattern, valid_password | 
					
						
							| 
									
										
										
										
											2023-06-09 11:36:38 +08:00
										 |  |  | from libs.rsa import generate_key_pair | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | from models.account import Tenant | 
					
						
							|  |  |  | from models.dataset import Dataset | 
					
						
							|  |  |  | from models.model import Account | 
					
						
							|  |  |  | from models.provider import Provider, ProviderModel | 
					
						
							| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  | from werkzeug.exceptions import NotFound | 
					
						
							| 
									
										
										
										
											2023-06-09 11:36:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | @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.') | 
					
						
							|  |  |  | def reset_password(email, new_password, password_confirm): | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Reset password of owner account | 
					
						
							|  |  |  |     Only available in SELF_HOSTED mode | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     if str(new_password).strip() != str(password_confirm).strip(): | 
					
						
							|  |  |  |         click.echo(click.style('sorry. The two passwords do not match.', fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     account = db.session.query(Account). \ | 
					
						
							|  |  |  |         filter(Account.email == email). \ | 
					
						
							|  |  |  |         one_or_none() | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     if not account: | 
					
						
							|  |  |  |         click.echo(click.style('sorry. the account: [{}] not exist .'.format(email), fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     try: | 
					
						
							|  |  |  |         valid_password(new_password) | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         click.echo( | 
					
						
							|  |  |  |             click.style('sorry. The passwords must match {} '.format(password_pattern), fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # generate password salt | 
					
						
							|  |  |  |     salt = secrets.token_bytes(16) | 
					
						
							|  |  |  |     base64_salt = base64.b64encode(salt).decode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # encrypt password with salt | 
					
						
							|  |  |  |     password_hashed = hash_password(new_password, salt) | 
					
						
							|  |  |  |     base64_password_hashed = base64.b64encode(password_hashed).decode() | 
					
						
							|  |  |  |     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.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.') | 
					
						
							|  |  |  | def reset_email(email, new_email, email_confirm): | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Replace account email | 
					
						
							|  |  |  |     :return: | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     if str(new_email).strip() != str(email_confirm).strip(): | 
					
						
							|  |  |  |         click.echo(click.style('Sorry, new email and confirm email do not match.', fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     account = db.session.query(Account). \ | 
					
						
							|  |  |  |         filter(Account.email == email). \ | 
					
						
							|  |  |  |         one_or_none() | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     if not account: | 
					
						
							|  |  |  |         click.echo(click.style('sorry. the account: [{}] not exist .'.format(email), fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     try: | 
					
						
							|  |  |  |         email_validate(new_email) | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         click.echo( | 
					
						
							|  |  |  |             click.style('sorry. {} is not a valid email. '.format(email), fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     account.email = new_email | 
					
						
							|  |  |  |     db.session.commit() | 
					
						
							|  |  |  |     click.echo(click.style('Congratulations!, email has been reset.', fg='green')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-09 11:36:38 +08:00
										 |  |  | @click.command('reset-encrypt-key-pair', help='Reset the asymmetric key pair of workspace for encrypt LLM credentials. ' | 
					
						
							|  |  |  |                                               'After the reset, all LLM credentials will become invalid, ' | 
					
						
							|  |  |  |                                               'requiring re-entry.' | 
					
						
							|  |  |  |                                               'Only support SELF_HOSTED mode.') | 
					
						
							|  |  |  | @click.confirmation_option(prompt=click.style('Are you sure you want to reset encrypt key pair?' | 
					
						
							|  |  |  |                                               ' this operation cannot be rolled back!', fg='red')) | 
					
						
							|  |  |  | def reset_encrypt_key_pair(): | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Reset the encrypted key pair of workspace for encrypt LLM credentials. | 
					
						
							|  |  |  |     After the reset, all LLM credentials will become invalid, requiring re-entry. | 
					
						
							|  |  |  |     Only support SELF_HOSTED mode. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-06-09 11:36:38 +08:00
										 |  |  |     if current_app.config['EDITION'] != 'SELF_HOSTED': | 
					
						
							|  |  |  |         click.echo(click.style('Sorry, only support SELF_HOSTED mode.', fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tenant = db.session.query(Tenant).first() | 
					
						
							|  |  |  |     if not tenant: | 
					
						
							|  |  |  |         click.echo(click.style('Sorry, no workspace found. Please enter /install to initialize.', fg='red')) | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tenant.encrypt_public_key = generate_key_pair(tenant.id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     db.session.query(Provider).filter(Provider.provider_type == 'custom').delete() | 
					
						
							| 
									
										
										
										
											2023-08-22 13:48:58 +08:00
										 |  |  |     db.session.query(ProviderModel).delete() | 
					
						
							| 
									
										
										
										
											2023-06-09 11:36:38 +08:00
										 |  |  |     db.session.commit() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     click.echo(click.style('Congratulations! ' | 
					
						
							|  |  |  |                            'the asymmetric key pair of workspace {} has been reset.'.format(tenant.id), fg='green')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  | @click.command('create-qdrant-indexes', help='Create qdrant indexes.') | 
					
						
							|  |  |  | def create_qdrant_indexes(): | 
					
						
							| 
									
										
										
										
											2024-01-30 14:33:48 +08:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Migrate other vector database datas to Qdrant. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  |     click.echo(click.style('Start create qdrant indexes.', fg='green')) | 
					
						
							|  |  |  |     create_count = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     page = 1 | 
					
						
							|  |  |  |     while True: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             datasets = db.session.query(Dataset).filter(Dataset.indexing_technique == 'high_quality') \ | 
					
						
							|  |  |  |                 .order_by(Dataset.created_at.desc()).paginate(page=page, per_page=50) | 
					
						
							|  |  |  |         except NotFound: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-02 23:42:00 +08:00
										 |  |  |         model_manager = ModelManager() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  |         page += 1 | 
					
						
							|  |  |  |         for dataset in datasets: | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  |             if dataset.index_struct_dict: | 
					
						
							|  |  |  |                 if dataset.index_struct_dict['type'] != 'qdrant': | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         click.echo('Create dataset qdrant index: {}'.format(dataset.id)) | 
					
						
							|  |  |  |                         try: | 
					
						
							| 
									
										
										
										
											2024-01-02 23:42:00 +08:00
										 |  |  |                             embedding_model = model_manager.get_model_instance( | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  |                                 tenant_id=dataset.tenant_id, | 
					
						
							| 
									
										
										
										
											2024-01-02 23:42:00 +08:00
										 |  |  |                                 provider=dataset.embedding_model_provider, | 
					
						
							|  |  |  |                                 model_type=ModelType.TEXT_EMBEDDING, | 
					
						
							|  |  |  |                                 model=dataset.embedding_model | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  |                             ) | 
					
						
							|  |  |  |                         except Exception: | 
					
						
							| 
									
										
										
										
											2024-01-28 19:59:06 +08:00
										 |  |  |                             continue | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  |                         embeddings = CacheEmbedding(embedding_model) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  |                         from core.index.vector_index.qdrant_vector_index import QdrantConfig, QdrantVectorIndex | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |                         index = QdrantVectorIndex( | 
					
						
							|  |  |  |                             dataset=dataset, | 
					
						
							|  |  |  |                             config=QdrantConfig( | 
					
						
							|  |  |  |                                 endpoint=current_app.config.get('QDRANT_URL'), | 
					
						
							|  |  |  |                                 api_key=current_app.config.get('QDRANT_API_KEY'), | 
					
						
							|  |  |  |                                 root_path=current_app.root_path | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                             embeddings=embeddings | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                         if index: | 
					
						
							|  |  |  |                             index.create_qdrant_dataset(dataset) | 
					
						
							|  |  |  |                             index_struct = { | 
					
						
							|  |  |  |                                 "type": 'qdrant', | 
					
						
							| 
									
										
										
										
											2023-09-18 18:15:41 +08:00
										 |  |  |                                 "vector_store": { | 
					
						
							|  |  |  |                                     "class_prefix": dataset.index_struct_dict['vector_store']['class_prefix']} | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  |                             } | 
					
						
							|  |  |  |                             dataset.index_struct = json.dumps(index_struct) | 
					
						
							|  |  |  |                             db.session.commit() | 
					
						
							|  |  |  |                             create_count += 1 | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             click.echo('passed.') | 
					
						
							|  |  |  |                     except Exception as e: | 
					
						
							|  |  |  |                         click.echo( | 
					
						
							| 
									
										
										
										
											2023-09-18 18:15:41 +08:00
										 |  |  |                             click.style('Create dataset index error: {} {}'.format(e.__class__.__name__, str(e)), | 
					
						
							|  |  |  |                                         fg='red')) | 
					
						
							| 
									
										
										
										
											2023-08-29 15:00:36 +08:00
										 |  |  |                         continue | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     click.echo(click.style('Congratulations! Create {} dataset indexes.'.format(create_count), fg='green')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | def register_commands(app): | 
					
						
							|  |  |  |     app.cli.add_command(reset_password) | 
					
						
							|  |  |  |     app.cli.add_command(reset_email) | 
					
						
							| 
									
										
										
										
											2023-06-09 11:36:38 +08:00
										 |  |  |     app.cli.add_command(reset_encrypt_key_pair) | 
					
						
							| 
									
										
										
										
											2023-08-24 21:27:31 +08:00
										 |  |  |     app.cli.add_command(create_qdrant_indexes) |