From f17772371127198878e52625942ca4f792de1b69 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Wed, 18 Sep 2024 19:19:27 -0700 Subject: [PATCH] Add default server configuration on first run in non-interactive mode This should configure Khoj with decent default configurations via Docker and avoid needing to configure Khoj via admin page to start using dockerized Khoj Update default max prompt size set during khoj initialization as online chat model are cheaper and offline chat models have larger context now --- docker-compose.yml | 2 +- src/khoj/main.py | 2 +- src/khoj/utils/cli.py | 6 +++ src/khoj/utils/initialization.py | 80 +++++++++++++++++++++----------- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1b79f784..4007b47f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,7 +48,7 @@ services: # Replace the domain with your domain. Proceed with caution, especially if you are using anonymous mode. # - KHOJ_NO_HTTPS=True # - KHOJ_DOMAIN=192.168.0.104 - command: --host="0.0.0.0" --port=42110 -vv --anonymous-mode + command: --host="0.0.0.0" --port=42110 -vv --anonymous-mode --non-interactive volumes: diff --git a/src/khoj/main.py b/src/khoj/main.py index 0a492e91..272c9bb8 100644 --- a/src/khoj/main.py +++ b/src/khoj/main.py @@ -131,7 +131,7 @@ def run(should_start_server=True): logger.info(f"📦 Initializing DB:\n{db_migrate_output.getvalue().strip()}") logger.debug(f"🌍 Initializing Web Client:\n{collectstatic_output.getvalue().strip()}") - initialization() + initialization(not args.non_interactive) # Create app directory, if it doesn't exist state.config_file.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/khoj/utils/cli.py b/src/khoj/utils/cli.py index dd3388d9..2e2bb77d 100644 --- a/src/khoj/utils/cli.py +++ b/src/khoj/utils/cli.py @@ -50,6 +50,12 @@ def cli(args=None): default=False, help="Run Khoj in anonymous mode. This does not require any login for connecting users.", ) + parser.add_argument( + "--non-interactive", + action="store_true", + default=False, + help="Start Khoj in non-interactive mode. Assumes interactive shell unavailable for config. E.g when run via Docker.", + ) args, remaining_args = parser.parse_known_args(args) diff --git a/src/khoj/utils/initialization.py b/src/khoj/utils/initialization.py index 79948ccb..3791af3f 100644 --- a/src/khoj/utils/initialization.py +++ b/src/khoj/utils/initialization.py @@ -15,11 +15,16 @@ from khoj.utils.constants import default_offline_chat_model, default_online_chat logger = logging.getLogger(__name__) -def initialization(): +def initialization(interactive: bool = True): def _create_admin_user(): logger.info( "👩‍✈️ Setting up admin user. These credentials will allow you to configure your server at /server/admin." ) + if not interactive and (not os.getenv("KHOJ_ADMIN_EMAIL") or not os.getenv("KHOJ_ADMIN_PASSWORD")): + logger.error( + "🚨 Admin user cannot be created. Please set the KHOJ_ADMIN_EMAIL, KHOJ_ADMIN_PASSWORD environment variables or start server in interactive mode." + ) + exit(1) email_addr = os.getenv("KHOJ_ADMIN_EMAIL") or input("Email: ") password = os.getenv("KHOJ_ADMIN_PASSWORD") or input("Password: ") admin_user = KhojUser.objects.create_superuser(email=email_addr, username=email_addr, password=password) @@ -27,23 +32,26 @@ def initialization(): def _create_chat_configuration(): logger.info( - "🗣️ Configure chat models available to your server. You can always update these at /server/admin using the credentials of your admin account" + "🗣️ Configure chat models available to your server. You can always update these at /server/admin using your admin account" ) try: - use_offline_model = input("Use offline chat model? (y/n): ") + use_offline_model = "y" if not interactive else input("Use offline chat model? (y/n): ") if use_offline_model == "y": logger.info("🗣️ Setting up offline chat model") - offline_chat_model = input( - f"Enter the offline chat model you want to use. See HuggingFace for available GGUF models (default: {default_offline_chat_model}): " - ) + if interactive: + offline_chat_model = input( + f"Enter the offline chat model you want to use. See HuggingFace for available GGUF models (default: {default_offline_chat_model}): " + ) + else: + offline_chat_model = "" if offline_chat_model == "": ChatModelOptions.objects.create( chat_model=default_offline_chat_model, model_type=ChatModelOptions.ModelType.OFFLINE ) else: - default_max_tokens = model_to_prompt_size.get(offline_chat_model, 2000) + default_max_tokens = model_to_prompt_size.get(offline_chat_model, 4000) max_tokens = input( f"Enter the maximum number of tokens to use for the offline chat model (default {default_max_tokens}):" ) @@ -66,40 +74,56 @@ def initialization(): except ModuleNotFoundError as e: logger.warning("Offline models are not supported on this device.") - use_openai_model = input("Use OpenAI models? (y/n): ") + default_openai_api_key = os.getenv("OPENAI_API_KEY") + default_use_openai_model = {True: "y", False: "n"}[default_openai_api_key != None] + use_openai_model = default_use_openai_model if not interactive else input("Use OpenAI models? (y/n): ") if use_openai_model == "y": logger.info("🗣️ Setting up your OpenAI configuration") - api_key = input("Enter your OpenAI API key: ") + if interactive: + api_key = input(f"Enter your OpenAI API key (default: {default_openai_api_key}): ") + else: + api_key = default_openai_api_key OpenAIProcessorConversationConfig.objects.create(api_key=api_key) - openai_chat_model = input( - f"Enter the OpenAI chat model you want to use (default: {default_online_chat_model}): " - ) - openai_chat_model = openai_chat_model or default_online_chat_model - - default_max_tokens = model_to_prompt_size.get(openai_chat_model, 2000) - max_tokens = input( - f"Enter the maximum number of tokens to use for the OpenAI chat model (default: {default_max_tokens}): " - ) - max_tokens = max_tokens or default_max_tokens + if interactive: + openai_chat_model = input( + f"Enter the OpenAI chat model you want to use (default: {default_online_chat_model}): " + ) + openai_chat_model = openai_chat_model or default_online_chat_model + else: + openai_chat_model = default_online_chat_model + default_max_tokens = model_to_prompt_size.get(openai_chat_model, 10000) + if interactive: + max_tokens = input( + f"Enter the maximum number of tokens to use for the OpenAI chat model (default: {default_max_tokens}): " + ) + max_tokens = max_tokens or default_max_tokens + else: + max_tokens = default_max_tokens ChatModelOptions.objects.create( chat_model=openai_chat_model, model_type=ChatModelOptions.ModelType.OPENAI, max_prompt_size=max_tokens ) default_speech2text_model = "whisper-1" - openai_speech2text_model = input( - f"Enter the OpenAI speech to text model you want to use (default: {default_speech2text_model}): " - ) - openai_speech2text_model = openai_speech2text_model or default_speech2text_model + if interactive: + openai_speech2text_model = input( + f"Enter the OpenAI speech to text model you want to use (default: {default_speech2text_model}): " + ) + openai_speech2text_model = openai_speech2text_model or default_speech2text_model + else: + openai_speech2text_model = default_speech2text_model SpeechToTextModelOptions.objects.create( model_name=openai_speech2text_model, model_type=SpeechToTextModelOptions.ModelType.OPENAI ) default_text_to_image_model = "dall-e-3" - openai_text_to_image_model = input( - f"Enter the OpenAI text to image model you want to use (default: {default_text_to_image_model}): " - ) - openai_text_to_image_model = openai_text_to_image_model or default_text_to_image_model + if interactive: + openai_text_to_image_model = input( + f"Enter the OpenAI text to image model you want to use (default: {default_text_to_image_model}): " + ) + openai_text_to_image_model = openai_text_to_image_model or default_text_to_image_model + else: + openai_text_to_image_model = default_text_to_image_model TextToImageModelConfig.objects.create( model_name=openai_text_to_image_model, model_type=TextToImageModelConfig.ModelType.OPENAI ) @@ -107,7 +131,7 @@ def initialization(): if use_offline_model == "y" or use_openai_model == "y": logger.info("🗣️ Chat model configuration complete") - use_offline_speech2text_model = input("Use offline speech to text model? (y/n): ") + use_offline_speech2text_model = "n" if not interactive else input("Use offline speech to text model? (y/n): ") if use_offline_speech2text_model == "y": logger.info("🗣️ Setting up offline speech to text model") # Delete any existing speech to text model options. There can only be one.