Source code for smarter.apps.account.management.commands.initialize_account

"""
Management command to initialize the Smarter platform or accounts.

This module defines the Django management command ``initialize_account`` for the Smarter platform.
It provides functionality to:

* Initialize the entire Smarter platform with minimal required resources.
* Initialize a single account, including:

  1. Creating an Account object.
  2. Creating, updating, or validating an admin user for the Account.
  3. Applying example manifests from GitHub.
  4. Adding plugin examples.
  5. Deploying builtin LLM clients.
  6. Creating StackAcademy (AI resource) examples.

It also supports initializing all existing accounts to update their shared AI resources.

Command-line Arguments
---------------------
* ``--account_number``: The account number that will own the remote API connection.
* ``--username``: Username for the admin account for this Account.
* ``--email``: Email address of the account admin user (fallback is ``<username>@<root_domain>``).
* ``--password``: Password for the account admin user (prompted if not supplied).
* ``--company_name``: Name of the company for the Account.
* ``--all``: If provided, initializes resources for all existing accounts.

Typical usage is for system administrators to initialize platform resources or bootstrap new accounts on the Smarter platform.
"""

from typing import Optional

from django.core.management import call_command

from smarter.apps.account.models import Account, User
from smarter.apps.account.utils import get_cached_smarter_admin_user_profile
from smarter.common.conf import smarter_settings
from smarter.lib import logging
from smarter.lib.django.management.base import SmarterCommand
from smarter.lib.django.validators import SmarterValidator

logger = logging.getLogger(__name__)


[docs] class Command(SmarterCommand): """ Django manage.py initialize_platform command. Initialize the Smarter platform. Creates the minimal resources necessary to start using Smarter. """
[docs] def initialize_account( self, account_number: str, username: str, email: str, password: Optional[str], company_name: Optional[str] ) -> bool: """ Initialize a single account with the provided information. Optionally creates or updates an account admin user with the provided username, email, and password. Creates a collection of shared AI resources that are owned by the account admin user. 1. Create the Account. 2. Create an admin user for the Account. 3. Apply example manifests from GitHub. 4. Add plugin examples. 5. Deploy builtin example llm_clients. 6. Create StackAcademy AI resources. .. note:: The typical use case is to pass the Smarter admin username (and no password) such that the shared AI resources are owned by the Smarter admin user. This removes some of the AI resource dependencies on the account, such as Connections and Secrets for some of the shared AI resources. """ msg = f"Initializing account {account_number} {company_name} with admin user {username}..." self.stdout.write(self.style.NOTICE(msg)) logger.info(msg) # 1. Create the Account. call_command("create_account", account_number=account_number, company_name=company_name) # 2. Create an admin user for the Account. try: user = User.objects.get(username=username) if password: user.set_password(password) user.email = email user.is_superuser = True user.is_staff = True user.save() msg = f"Updated the existing User '{username}' for account {account_number}." self.stdout.write(self.style.NOTICE(msg)) logger.info(msg) else: msg = f"User '{username}' already exists for account {account_number}. No password provided, so the existing password will be left unchanged." self.stdout.write(self.style.WARNING(msg)) logger.warning(msg) except User.DoesNotExist: if not password: msg = ( "No password was provided. Using a random value. You will need to reset the password for this user." ) self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return False call_command( "create_user", account_number=account_number, username=username, email=email, password=password, first_name="Account", last_name="Admin", admin=True, ) # pylint: disable=broad-except except Exception as e: msg = f"Failed to create or update the admin user for account {account_number}: {str(e)}" self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return False # 3. Apply example manifests from GitHub. call_command( "load_from_github", account_number=account_number, url="https://github.com/QueriumCorp/smarter-demo" ) call_command( "load_from_github", account_number=account_number, url="https://github.com/smarter-sh/examples", repo_version=2, ) # 4. Add builtin plugin examples and deploy llm_clients. call_command("add_plugin_examples", username=username) call_command("deploy_example_llm_client", account_number=account_number) call_command("deploy_builtin_llm_clients", account_number=account_number) # 5. Setup Stackademy AI resources, used for training and testing. call_command("create_stackademy", account_number=account_number) logger.info("Account %s initialized successfully.", account_number) return True
[docs] def initialize_all_accounts(self) -> bool: """ Initialize all EXISTING accounts. This is useful for ensuring that all accounts have the the most recent versions of shared AI resources. """ retval = True accounts = Account.objects.all() smarter_admin_user_profile = get_cached_smarter_admin_user_profile() for account in accounts: try: self.initialize_account( account_number=account.account_number, username=smarter_admin_user_profile.user.username, email=f"{account.account_number}_admin@{smarter_settings.root_domain}", password=None, company_name=account.company_name, ) # pylint: disable=broad-except except Exception as e: msg = f"Failed to initialize account {account.account_number}: {str(e)}" self.stdout.write(self.style.ERROR(msg)) logger.error(msg) retval = False continue return retval
[docs] def add_arguments(self, parser): """Add arguments to the command.""" parser.add_argument( "--account_number", type=str, help="The account number that will own the remote Api connection. Defaults to smarter_test_api", ) parser.add_argument( "--username", type=str, help="The username for the admin account for this Account.", ) parser.add_argument("--email", type=str, help="The email address of the user to associate with this Secret.") parser.add_argument( "--password", type=str, help="The value to encrypt and persist. If not provided, you will be prompted." ) parser.add_argument( "--company_name", type=str, help="The company name for the Account.", ) parser.add_argument( "--all", action="store_true", help="Initialize all accounts.", )
[docs] def handle(self, *args, **options): """ Initialize the Smarter platform. Creates the minimal resources necessary to start using Smarter. 1. Create the Account. 2. Create an admin user for the Account. 3. Apply example manifests from GitHub. 4. Add plugin examples. 5. Deploy builtin example llm_clients. 6. Create StackAcademy AI resources. """ self.handle_begin() all_accounts = options.get("all") if all_accounts: if self.initialize_all_accounts(): self.handle_completed_success() else: self.handle_completed_failure(msg="Failed to initialize all accounts.") return # ---------------------------------------------------------------------- # we're only initializing a single account. # ---------------------------------------------------------------------- account_number = options.get("account_number") if not account_number: msg = "account number is required." self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return if not SmarterValidator.is_valid_account_number(account_number): msg = f"account number {account_number} is not valid." self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return username = options.get("username") if not username: msg = "username is required." self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return if not SmarterValidator.is_valid_username(username): msg = f"username {username} is not valid." self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return email = options.get("email") if not email: email = f"{username}@{smarter_settings.root_domain}" msg = f"No email provided, using the default value: {email}" self.stdout.write(self.style.WARNING(msg)) logger.warning(msg) if not SmarterValidator.is_valid_email(email): email = f"{username}@{smarter_settings.root_domain}" msg = f"No email provided, using the default value: {email}" self.stdout.write(self.style.WARNING(msg)) logger.warning(msg) password = options.get("password") if not password: msg = "password is required." self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return company_name = options.get("company_name") if not company_name: msg = "company name is required." self.stdout.write(self.style.ERROR(msg)) logger.error(msg) self.handle_completed_failure(msg=msg) return self.initialize_account( account_number=account_number, username=username, email=email, password=password, company_name=company_name, ) self.handle_completed_success()