From e385df4599211522f597cd1ee2c590daad75a5c2 Mon Sep 17 00:00:00 2001
From: ali asaria <ali.asaria@gmail.com>
Date: Tue, 5 Mar 2024 09:32:29 -0500
Subject: [PATCH] update autosetup and connect with more detailed steps

---
 package-lock.json                             |   8 +-
 package.json                                  |   1 +
 src/main/main.ts                              |  40 +-
 src/main/preload.ts                           |   6 +-
 src/main/util.ts                              | 115 ++++-
 .../components/Connect/LocalConnection.tsx    | 442 +++++++++++++++---
 .../components/Connect/LoginModal.tsx         |  14 +-
 7 files changed, 522 insertions(+), 104 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index afaeadca..4707df24 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,7 +5,7 @@
   "requires": true,
   "packages": {
     "": {
-      "version": "0.1.6",
+      "version": "0.2.2",
       "hasInstallScript": true,
       "license": "MIT",
       "dependencies": {
@@ -26,6 +26,7 @@
         "@uppy/xhr-upload": "^3.3.2",
         "basic-auth": "~2.0.1",
         "cidr-matcher": "^2.1.1",
+        "command-exists": "^1.2.9",
         "debug": "^4.3.4",
         "easy-peasy": "^6.0.2",
         "electron-debug": "^3.2.0",
@@ -7438,6 +7439,11 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/command-exists": {
+      "version": "1.2.9",
+      "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
+      "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
+    },
     "node_modules/commander": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
diff --git a/package.json b/package.json
index 3e2abe12..a7dbd875 100644
--- a/package.json
+++ b/package.json
@@ -106,6 +106,7 @@
     "@uppy/xhr-upload": "^3.3.2",
     "basic-auth": "~2.0.1",
     "cidr-matcher": "^2.1.1",
+    "command-exists": "^1.2.9",
     "debug": "^4.3.4",
     "easy-peasy": "^6.0.2",
     "electron-debug": "^3.2.0",
diff --git a/src/main/main.ts b/src/main/main.ts
index 25153181..f15c7b78 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -23,6 +23,9 @@ import {
   installLocalServer,
   killLocalServer,
   executeInstallStep,
+  checkIfShellCommandExists,
+  checkIfCondaEnvironmentExists,
+  checkDependencies,
 } from './util';
 
 // ////////////
@@ -68,19 +71,42 @@ ipcMain.handle('server:InstallLocally', (event) => {
 });
 
 ipcMain.handle('server:install_download', (event) => {
-  return executeInstallStep('download_transformer_lab');
+  return executeInstallStep('download_transformer_lab', false);
 });
 
-ipcMain.handle('server:install_conda', (event) => {
-  return executeInstallStep('install_conda');
+ipcMain.handle('server:install_conda', async (event) => {
+  console.log('** Installing conda');
+  await executeInstallStep('install_conda', true);
+  console.log('Finishing installing conda');
+  return;
 });
 
-ipcMain.handle('server:install_create-conda-environment', (event) => {
-  return executeInstallStep('create_conda_environment');
+ipcMain.handle('server:install_create-conda-environment', async (event) => {
+  return executeInstallStep('create_conda_environment', true);
 });
 
-ipcMain.handle('server:install_install-dependencies', (event) => {
-  return executeInstallStep('install_dependencies');
+ipcMain.handle('server:install_install-dependencies', async (event) => {
+  return executeInstallStep('install_dependencies', true);
+});
+
+ipcMain.handle('server:checkIfCondaExists', async (event) => {
+  const r = checkIfShellCommandExists('conda');
+  console.log('conda exists', r);
+  return r;
+});
+
+ipcMain.handle('server:checkIfCondaEnvironmentExists', async (event) => {
+  const envList = await checkIfCondaEnvironmentExists();
+  console.log('envList', envList);
+  return envList;
+});
+
+ipcMain.handle('server:checkIfUvicornExists', async (event) => {
+  return checkIfShellCommandExists('uvicorn');
+});
+
+ipcMain.handle('server:checkDependencies', async (event) => {
+  return await checkDependencies();
 });
 
 class AppUpdater {
diff --git a/src/main/preload.ts b/src/main/preload.ts
index 0728a13c..9ecb63c0 100644
--- a/src/main/preload.ts
+++ b/src/main/preload.ts
@@ -21,7 +21,11 @@ export type Channels =
   | 'server:install_download'
   | 'server:install_conda'
   | 'server:install_create-conda-environment'
-  | 'server:install_install-dependencies';
+  | 'server:install_install-dependencies'
+  | 'server:checkIfCondaExists'
+  | 'server:checkIfCondaEnvironmentExists'
+  | 'server:checkIfUvicornExists'
+  | 'server:checkDependencies';
 
 const electronHandler = {
   ipcRenderer: {
diff --git a/src/main/util.ts b/src/main/util.ts
index 1bb738ce..db58b94b 100644
--- a/src/main/util.ts
+++ b/src/main/util.ts
@@ -4,8 +4,11 @@ import path from 'path';
 const fs = require('fs');
 const os = require('os');
 const { spawn, exec, ChildProcess } = require('child_process');
+const util = require('node:util');
+const awaitExec = util.promisify(require('node:child_process').exec);
 const homeDir = os.homedir();
 const transformerLabDir = path.join(homeDir, '.transformerlab/src/');
+const commandExistsSync = require('command-exists').sync;
 
 var localServer: typeof ChildProcess = null;
 
@@ -124,24 +127,104 @@ export function installLocalServer() {
   }
 }
 
-export function executeInstallStep(argument: string) {
-  console.log('Downloading transformerlab-api to ~/.transformerlab/src');
+export function checkIfShellCommandExists(command: string) {
+  if (commandExistsSync(command)) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+export async function checkDependencies() {
+  // First activate the transformerlab environment
+  // Then run pip list
+  // Then compare the output to the list of dependencies
+  // If any are missing, return the missing ones
+  // If all are present, manually check if the uvicorn command is present
+  const uvicornExists = checkIfShellCommandExists('uvicorn');
 
+  const command =
+    'eval "$(conda shell.bash hook)" && conda activate transformerlab && pip list --format json';
   const options = { shell: '/bin/bash' };
-  try {
-    const child = exec(
-      `curl https://raw.githubusercontent.com/transformerlab/transformerlab-api/main/install.sh | bash -s -- ${argument}`,
-      options,
-      (error, stdout, stderr) => {
-        if (error) {
-          console.error(`exec error: ${error}`);
-          return;
-        }
-        console.log(`stdout: ${stdout}`);
-        console.error(`stderr: ${stderr}`);
-      }
+  const { stdout, stderr } = await awaitExec(command, options).catch((err) => {
+    console.log('Error running pip list', err);
+  });
+  console.log('stdout:', stdout);
+  console.error('stderr:', stderr);
+
+  const pipList = JSON.parse(stdout);
+  const pipListNames = pipList.map((x) => x.name);
+  const keyDependencies = [
+    'fastapi',
+    'pydantic',
+    'transformers',
+    'uvicorn',
+    'sentencepiece',
+    'torch',
+    'transformers',
+    'peft',
+    'packaging',
+    'fschat',
+  ];
+
+  //compare the list of dependencies to the keyDependencies
+  let missingDependencies = [];
+  for (let i = 0; i < keyDependencies.length; i++) {
+    if (!pipListNames.includes(keyDependencies[i])) {
+      missingDependencies.push(keyDependencies[i]);
+    }
+  }
+
+  console.log('missingDependencies', missingDependencies);
+  return missingDependencies;
+}
+
+export async function checkIfCondaEnvironmentExists() {
+  const options = { shell: '/bin/bash' };
+  console.log('Checking if Conda environment "transformerlab" exists');
+  const { stdout, stderr } = await awaitExec(`conda env list`, options).catch(
+    (err) => {
+      console.log('Error running conda env list', err);
+    }
+  );
+  console.log('stdout:', stdout);
+  console.error('stderr:', stderr);
+
+  // search for the string "transformerlab" in the output
+  if (stdout.includes('transformerlab')) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+export async function executeInstallStep(
+  argument: string,
+  useLocalInstallSh = false
+) {
+  const options = { shell: '/bin/bash' };
+  console.log('Running install.sh ' + argument);
+
+  if (useLocalInstallSh) {
+    console.log(
+      `Using local install.sh and running: ~/.transformerlab/src/install.sh ${argument}`
     );
-  } catch (err) {
-    console.log('Failed to download Transformer Lab API', err);
+    const { stdout, stderr } = await awaitExec(
+      `~/.transformerlab/src/install.sh ${argument}`,
+      options
+    ).catch((err) => {
+      console.log('Error running install.sh', err);
+    });
+    console.log('stdout:', stdout);
+    console.error('stderr:', stderr);
+  } else {
+    const { stdout, stderr } = await awaitExec(
+      `curl https://raw.githubusercontent.com/transformerlab/transformerlab-api/main/install.sh | bash -s -- ${argument}`,
+      options
+    ).catch((err) => {
+      console.log('Error running install.sh', err);
+    });
+    console.log('stdout:', stdout);
+    console.error('stderr:', stderr);
   }
 }
diff --git a/src/renderer/components/Connect/LocalConnection.tsx b/src/renderer/components/Connect/LocalConnection.tsx
index 187f166d..05c533c7 100644
--- a/src/renderer/components/Connect/LocalConnection.tsx
+++ b/src/renderer/components/Connect/LocalConnection.tsx
@@ -34,18 +34,28 @@ function setIntervalXTimes(callback, notSuccessful, delay, repetitions) {
   }, delay);
 }
 
+const Steps = [
+  'CHECK_IF_INSTALLED',
+  'CHECK_VERSION',
+  'CHECK_IF_CONDA_INSTALLED',
+  'CHECK_IF_CONDA_ENVIRONMENT_EXISTS',
+  'CHECK_IF_PYTHON_DEPENDENCIES_INSTALLED',
+  'CHECK_IF_SERVER_RUNNING_ON_PORT_8000',
+  'CHECK_FOR_IMPORTANT_PLUGINS',
+];
+
 function CheckIfInstalled({ activeStep, setActiveStep }) {
   const [installStatus, setInstallStatus] = useState('notstarted'); // notstarted, pending, success, error
 
   useEffect(() => {
-    if (activeStep !== 1) return;
+    if (activeStep !== Steps.indexOf('CHECK_IF_INSTALLED')) return;
     (async () => {
       const serverIsInstalled = await window.electron.ipcRenderer.invoke(
         'server:checkIfInstalledLocally'
       );
       if (serverIsInstalled) {
         setInstallStatus('success');
-        setActiveStep(2);
+        setActiveStep(Steps.indexOf('CHECK_VERSION'));
       } else {
         setInstallStatus('notstarted');
       }
@@ -68,38 +78,39 @@ function CheckIfInstalled({ activeStep, setActiveStep }) {
         {installStatus === 'error' && <Chip color="danger">Error </Chip>}
 
         <ButtonGroup variant="plain" spacing={1}>
-          {activeStep == 1 && installStatus == 'notstarted' && (
-            <Button
-              variant="solid"
-              onClick={async () => {
-                await window.electron.ipcRenderer.invoke(
-                  'server:InstallLocally'
-                );
-                setInstallStatus('pending');
-                setIntervalXTimes(
-                  async () => {
-                    const serverIsInstalled =
-                      await window.electron.ipcRenderer.invoke(
-                        'server:checkIfInstalledLocally'
-                      );
-                    if (serverIsInstalled) {
-                      setInstallStatus('success');
-                      setActiveStep(2);
-                      return true;
-                    }
-                    return false;
-                  },
-                  () => {
-                    setInstallStatus('error');
-                  },
-                  2000,
-                  8
-                );
-              }}
-            >
-              Install Transformer Lab Server API
-            </Button>
-          )}
+          {activeStep == Steps.indexOf('CHECK_IF_INSTALLED') &&
+            installStatus == 'notstarted' && (
+              <Button
+                variant="solid"
+                onClick={async () => {
+                  await window.electron.ipcRenderer.invoke(
+                    'server:InstallLocally'
+                  );
+                  setInstallStatus('pending');
+                  setIntervalXTimes(
+                    async () => {
+                      const serverIsInstalled =
+                        await window.electron.ipcRenderer.invoke(
+                          'server:checkIfInstalledLocally'
+                        );
+                      if (serverIsInstalled) {
+                        setInstallStatus('success');
+                        setActiveStep(Steps.indexOf('CHECK_IF_INSTALLED') + 1);
+                        return true;
+                      }
+                      return false;
+                    },
+                    () => {
+                      setInstallStatus('error');
+                    },
+                    2000,
+                    8
+                  );
+                }}
+              >
+                Install Transformer Lab Server API
+              </Button>
+            )}
         </ButtonGroup>
       </Stack>
     </>
@@ -112,7 +123,7 @@ function CheckCurrentVersion({ activeStep, setActiveStep }) {
   const [installStatus, setInstallStatus] = useState('notstarted'); // notstarted, pending, success, error
 
   useEffect(() => {
-    if (activeStep !== 2) return;
+    if (activeStep !== Steps.indexOf('CHECK_VERSION')) return;
 
     (async () => {
       const ver = await window.electron.ipcRenderer.invoke(
@@ -129,7 +140,7 @@ function CheckCurrentVersion({ activeStep, setActiveStep }) {
       setRelease(tag);
 
       if (ver === tag) {
-        setActiveStep(3);
+        setActiveStep(Steps.indexOf('CHECK_VERSION') + 1);
       }
     })();
   }, [activeStep]);
@@ -137,7 +148,7 @@ function CheckCurrentVersion({ activeStep, setActiveStep }) {
   return (
     <>
       <Stack spacing={1}>
-        {activeStep >= 2 && (
+        {activeStep >= Steps.indexOf('CHECK_VERSION') && (
           <>
             <Typography level="body-sm">
               Your version of Transformer Lab API is {version}
@@ -152,7 +163,7 @@ function CheckCurrentVersion({ activeStep, setActiveStep }) {
         )}
         {version == release && <Chip color="success">Success!</Chip>}
 
-        {activeStep == 2 && release != '' && (
+        {activeStep == Steps.indexOf('CHECK_VERSION') && release != '' && (
           <ButtonGroup variant="plain" spacing={1}>
             <Button
               variant="solid"
@@ -171,7 +182,7 @@ function CheckCurrentVersion({ activeStep, setActiveStep }) {
                     if (ver === release) {
                       setInstallStatus('success');
                       setVersion(ver);
-                      setActiveStep(3);
+                      setActiveStep(Steps.indexOf('CHECK_VERSION') + 1);
                       return true;
                     }
                     return false;
@@ -190,7 +201,7 @@ function CheckCurrentVersion({ activeStep, setActiveStep }) {
               variant="plain"
               size="sm"
               onClick={() => {
-                setActiveStep(3);
+                setActiveStep(Steps.indexOf('CHECK_VERSION') + 1);
               }}
             >
               Skip
@@ -211,17 +222,20 @@ function RunServer({ activeStep, setActiveStep }) {
   } = useCheckLocalConnection();
 
   useEffect(() => {
-    if (activeStep !== 3) return;
+    if (activeStep !== Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000'))
+      return;
 
     if (server && !serverError) {
       console.log('I think things are good');
-      setActiveStep(4);
+      setActiveStep(Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000') + 1);
       return;
     } else {
       setIntervalXTimes(
         async () => {
           if (!server || serverError) return false;
-          setActiveStep(4);
+          setActiveStep(
+            Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000') + 1
+          );
           return true;
         },
         () => {},
@@ -234,14 +248,14 @@ function RunServer({ activeStep, setActiveStep }) {
   return (
     <>
       <Stack spacing={1}>
-        {activeStep >= 3 && server && !serverError && (
-          <Chip color="success">Success!</Chip>
-        )}
-        {activeStep >= 3 && (!server || serverError) && (
-          <Chip color="danger">Not Running</Chip>
-        )}
+        {activeStep >= Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000') &&
+          server &&
+          !serverError && <Chip color="success">Success!</Chip>}
+        {activeStep >= Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000') &&
+          (!server || serverError) && <Chip color="danger">Not Running</Chip>}
         <ButtonGroup variant="plain" spacing={1}>
-          {activeStep == 3 &&
+          {activeStep ==
+            Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000') &&
             (!server || serverError ? (
               thinking ? (
                 <CircularProgress color="primary" />
@@ -257,7 +271,8 @@ function RunServer({ activeStep, setActiveStep }) {
                         );
 
                       if (start_process?.status == 'error') {
-                        const response_text = "Failed to start server: \n" + start_process?.message;
+                        const response_text =
+                          'Failed to start server: \n' + start_process?.message;
                         alert(response_text);
                         setThinking(false);
                         return;
@@ -267,7 +282,11 @@ function RunServer({ activeStep, setActiveStep }) {
                         async () => {
                           if (!server || serverError) return false;
                           setThinking(false);
-                          setActiveStep(4);
+                          setActiveStep(
+                            Steps.indexOf(
+                              'CHECK_IF_SERVER_RUNNING_ON_PORT_8000'
+                            ) + 1
+                          );
                           return true;
                         },
                         () => {
@@ -296,7 +315,7 @@ function CheckForPlugins({ activeStep, setActiveStep }) {
   const [installing, setInstalling] = useState(false);
 
   useEffect(() => {
-    if (activeStep !== 4) return;
+    if (activeStep !== Steps.indexOf('CHECK_FOR_IMPORTANT_PLUGINS')) return;
 
     (async () => {
       const p = await fetch(
@@ -306,7 +325,7 @@ function CheckForPlugins({ activeStep, setActiveStep }) {
       setMissingPlugins(json);
 
       if (json.length === 0) {
-        setActiveStep(5);
+        setActiveStep(Steps.indexOf('CHECK_FOR_IMPORTANT_PLUGINS') + 1);
       }
     })();
   }, [activeStep]);
@@ -334,7 +353,7 @@ function CheckForPlugins({ activeStep, setActiveStep }) {
           {missingPlugins?.map((p) => p).join(', ')}
         </Typography>
 
-        {activeStep == 4 && (
+        {activeStep == Steps.indexOf('CHECK_FOR_IMPORTANT_PLUGINS') && (
           <ButtonGroup variant="plain" spacing={1}>
             <Button
               variant="solid"
@@ -346,7 +365,7 @@ function CheckForPlugins({ activeStep, setActiveStep }) {
                 );
                 setInstalling(false);
                 setMissingPlugins([]);
-                setActiveStep(5);
+                setActiveStep(Steps.indexOf('CHECK_FOR_IMPORTANT_PLUGINS') + 1);
                 // setIntervalXTimes(
                 //   async () => {
                 //     const p = await fetch(
@@ -377,7 +396,9 @@ function CheckForPlugins({ activeStep, setActiveStep }) {
               <Button
                 variant="plain"
                 onClick={() => {
-                  setActiveStep(5);
+                  setActiveStep(
+                    Steps.indexOf('CHECK_FOR_IMPORTANT_PLUGINS') + 1
+                  );
                 }}
                 size="sm"
               >
@@ -408,6 +429,237 @@ function ConnectToLocalServer({ activeStep, setActiveStep, tryToConnect }) {
   );
 }
 
+function CheckIfCondaInstalled({ activeStep, setActiveStep }) {
+  const [installStatus, setInstallStatus] = useState(''); // notstarted, pending, success, error
+
+  useEffect(() => {
+    if (activeStep !== Steps.indexOf('CHECK_IF_CONDA_INSTALLED')) return;
+
+    (async () => {
+      const condaExists = await window.electron.ipcRenderer.invoke(
+        'server:checkIfCondaExists'
+      );
+      if (condaExists) {
+        setInstallStatus('success');
+        setActiveStep(Steps.indexOf('CHECK_IF_CONDA_INSTALLED') + 1);
+      } else {
+        setInstallStatus('notstarted');
+      }
+    })();
+  }, [activeStep]);
+
+  return (
+    <>
+      <Stack spacing={1}>
+        {installStatus == 'success' && <Chip color="success">Success!</Chip>}
+        {installStatus == 'pending' && <CircularProgress color="primary" />}
+
+        {activeStep == Steps.indexOf('CHECK_IF_CONDA_INSTALLED') &&
+          installStatus == 'notstarted' && (
+            <ButtonGroup variant="plain" spacing={1}>
+              <Button
+                variant="solid"
+                size="sm"
+                startDecorator={<RotateCcwIcon size="16px" />}
+                onClick={async () => {
+                  setInstallStatus('pending');
+                  const installConda = await window.electron.ipcRenderer.invoke(
+                    'server:install_conda'
+                  );
+                  const condaExists = await window.electron.ipcRenderer.invoke(
+                    'server:checkIfCondaExists'
+                  );
+                  if (condaExists) {
+                    setInstallStatus('success');
+                    setActiveStep(
+                      Steps.indexOf('CHECK_IF_CONDA_INSTALLED') + 1
+                    );
+                    return;
+                  }
+                  setIntervalXTimes(
+                    async () => {
+                      const condaExists =
+                        await window.electron.ipcRenderer.invoke(
+                          'server:checkIfCondaExists'
+                        );
+                      if (condaExists) {
+                        setInstallStatus('success');
+                        setActiveStep(
+                          Steps.indexOf('CHECK_IF_CONDA_INSTALLED') + 1
+                        );
+                        return true;
+                      }
+                      return false;
+                    },
+                    () => {},
+                    2000,
+                    8
+                  );
+                }}
+              >
+                Install Conda
+              </Button>
+            </ButtonGroup>
+          )}
+      </Stack>
+    </>
+  );
+}
+
+function CheckIfCondaEnvironmentExists({ activeStep, setActiveStep }) {
+  const [installStatus, setInstallStatus] = useState(''); // notstarted, pending, success, error
+
+  useEffect(() => {
+    if (activeStep !== Steps.indexOf('CHECK_IF_CONDA_ENVIRONMENT_EXISTS'))
+      return;
+
+    (async () => {
+      const condaExists = await window.electron.ipcRenderer.invoke(
+        'server:checkIfCondaEnvironmentExists'
+      );
+      if (condaExists) {
+        setInstallStatus('success');
+        setActiveStep(Steps.indexOf('CHECK_IF_CONDA_ENVIRONMENT_EXISTS') + 1);
+      } else {
+        setInstallStatus('notstarted');
+      }
+    })();
+  }, [activeStep]);
+
+  return (
+    <>
+      <Stack spacing={1}>
+        {installStatus == 'success' && <Chip color="success">Success!</Chip>}
+        {installStatus == 'pending' && <CircularProgress color="primary" />}
+
+        {activeStep == Steps.indexOf('CHECK_IF_CONDA_ENVIRONMENT_EXISTS') &&
+          installStatus == 'notstarted' && (
+            <ButtonGroup variant="plain" spacing={1}>
+              <Button
+                variant="solid"
+                size="sm"
+                startDecorator={<RotateCcwIcon size="16px" />}
+                onClick={async () => {
+                  setInstallStatus('pending');
+                  const installConda = await window.electron.ipcRenderer.invoke(
+                    'server:install_create-conda-environment'
+                  );
+                  const condaExists = await window.electron.ipcRenderer.invoke(
+                    'server:checkIfCondaEnvironmentExists'
+                  );
+                  if (condaExists) {
+                    setInstallStatus('success');
+                    setActiveStep(
+                      Steps.indexOf('CHECK_IF_CONDA_ENVIRONMENT_EXISTS') + 1
+                    );
+                    return;
+                  }
+                  setIntervalXTimes(
+                    async () => {
+                      const condaExists =
+                        await window.electron.ipcRenderer.invoke(
+                          'server:checkIfCondaEnvironmentExists'
+                        );
+                      if (condaExists) {
+                        setInstallStatus('success');
+                        setActiveStep(
+                          Steps.indexOf('CHECK_IF_CONDA_ENVIRONMENT_EXISTS') + 1
+                        );
+                        return true;
+                      }
+                      return false;
+                    },
+                    () => {},
+                    2000,
+                    8
+                  );
+                }}
+              >
+                Create "transformerlab" Conda Environment
+              </Button>
+            </ButtonGroup>
+          )}
+      </Stack>
+    </>
+  );
+}
+
+function CheckDependencies({ activeStep, setActiveStep }) {
+  const [installStatus, setInstallStatus] = useState(''); // notstarted, pending, success, error
+  const [missingDependencies, setMissingDependencies] = useState([]);
+
+  useEffect(() => {
+    if (activeStep !== Steps.indexOf('CHECK_IF_PYTHON_DEPENDENCIES_INSTALLED'))
+      return;
+
+    (async () => {
+      const missingDependencies = await window.electron.ipcRenderer.invoke(
+        'server:checkDependencies'
+      );
+      if (missingDependencies.length == 0) {
+        setInstallStatus('success');
+        setActiveStep(
+          Steps.indexOf('CHECK_IF_PYTHON_DEPENDENCIES_INSTALLED') + 1
+        );
+      } else {
+        setMissingDependencies(missingDependencies);
+        setInstallStatus('notstarted');
+      }
+    })();
+  }, [activeStep]);
+
+  return (
+    <>
+      <Stack spacing={1}>
+        {installStatus == 'success' && <Chip color="success">Success!</Chip>}
+        {installStatus == 'pending' && <CircularProgress color="primary" />}
+        {missingDependencies.length > 0 && installStatus == 'notstarted' && (
+          <Typography level="body-sm" color="warning">
+            Many dependencies are missing including:{' '}
+            <Typography level="body-sm" color="warning">
+              {missingDependencies.join(', ')} ...
+            </Typography>
+          </Typography>
+        )}
+
+        {activeStep ==
+          Steps.indexOf('CHECK_IF_PYTHON_DEPENDENCIES_INSTALLED') &&
+          installStatus == 'notstarted' && (
+            <ButtonGroup variant="plain" spacing={1}>
+              <Button
+                variant="solid"
+                size="sm"
+                startDecorator={<RotateCcwIcon size="16px" />}
+                onClick={async () => {
+                  setInstallStatus('pending');
+                  const installDependencies =
+                    await window.electron.ipcRenderer.invoke(
+                      'server:install_install-dependencies'
+                    );
+
+                  const missingDependencies =
+                    await window.electron.ipcRenderer.invoke(
+                      'server:checkDependencies'
+                    );
+                  if (missingDependencies.length == 0) {
+                    setInstallStatus('success');
+                    setActiveStep(
+                      Steps.indexOf('CHECK_IF_PYTHON_DEPENDENCIES_INSTALLED') +
+                        1
+                    );
+                    return;
+                  }
+                }}
+              >
+                Install Dependencies
+              </Button>
+            </ButtonGroup>
+          )}
+      </Stack>
+    </>
+  );
+}
+
 function InstallStep({ children, thisStep, title, activeStep, setActiveStep }) {
   return (
     <Step
@@ -429,21 +681,33 @@ function InstallStep({ children, thisStep, title, activeStep, setActiveStep }) {
 }
 
 function InstallStepper({ setServer }) {
-  const [activeStep, setActiveStep] = useState(1); // 0, 1, 2
+  const [activeStep, setActiveStep] = useState(
+    Steps.indexOf('CHECK_IF_INSTALLED')
+  ); // 0, 1, 2
 
   function tryToConnect() {
     const fullServer = 'http://' + 'localhost' + ':' + '8000' + '/';
     window.TransformerLab = {};
     window.TransformerLab.API_URL = fullServer;
-    setActiveStep(1);
+    setActiveStep(Steps.indexOf('CHECK_IF_INSTALLED'));
     setServer(fullServer);
   }
   return (
-    <>
-      <Stepper orientation="vertical">
+    <Sheet
+      sx={{
+        display: 'flex',
+        flexDirection: 'column',
+        overflow: 'hidden',
+        height: '100%',
+      }}
+    >
+      <Stepper
+        orientation="vertical"
+        sx={{ display: 'flex', overflow: 'auto' }}
+      >
         {/* Active Step: {activeStep} */}
         <InstallStep
-          thisStep={1}
+          thisStep={Steps.indexOf('CHECK_IF_INSTALLED')}
           title="Check if Server is Installed at ~/.transformerlab/"
           activeStep={activeStep}
           setActiveStep={setActiveStep}
@@ -454,7 +718,7 @@ function InstallStepper({ setServer }) {
           />
         </InstallStep>
         <InstallStep
-          thisStep={2}
+          thisStep={Steps.indexOf('CHECK_VERSION')}
           title="Check Current Version"
           activeStep={activeStep}
           setActiveStep={setActiveStep}
@@ -465,7 +729,40 @@ function InstallStepper({ setServer }) {
           />
         </InstallStep>
         <InstallStep
-          thisStep={3}
+          thisStep={Steps.indexOf('CHECK_IF_CONDA_INSTALLED')}
+          title="Check if Conda is Installed"
+          activeStep={activeStep}
+          setActiveStep={setActiveStep}
+        >
+          <CheckIfCondaInstalled
+            activeStep={activeStep}
+            setActiveStep={setActiveStep}
+          />
+        </InstallStep>
+        <InstallStep
+          thisStep={Steps.indexOf('CHECK_IF_CONDA_ENVIRONMENT_EXISTS')}
+          title="Check if Conda Environment 'transformerlab' Exists"
+          activeStep={activeStep}
+          setActiveStep={setActiveStep}
+        >
+          <CheckIfCondaEnvironmentExists
+            activeStep={activeStep}
+            setActiveStep={setActiveStep}
+          />
+        </InstallStep>
+        <InstallStep
+          thisStep={Steps.indexOf('CHECK_IF_PYTHON_DEPENDENCIES_INSTALLED')}
+          title="Check if Python Dependencies are Installed"
+          activeStep={activeStep}
+          setActiveStep={setActiveStep}
+        >
+          <CheckDependencies
+            activeStep={activeStep}
+            setActiveStep={setActiveStep}
+          />
+        </InstallStep>
+        <InstallStep
+          thisStep={Steps.indexOf('CHECK_IF_SERVER_RUNNING_ON_PORT_8000')}
           title="Check if Server is Running Locally on Port 8000"
           activeStep={activeStep}
           setActiveStep={setActiveStep}
@@ -473,7 +770,7 @@ function InstallStepper({ setServer }) {
           <RunServer activeStep={activeStep} setActiveStep={setActiveStep} />
         </InstallStep>
         <InstallStep
-          thisStep={4}
+          thisStep={Steps.indexOf('CHECK_FOR_IMPORTANT_PLUGINS')}
           title="Check for Important Plugins"
           activeStep={activeStep}
           setActiveStep={setActiveStep}
@@ -491,25 +788,18 @@ function InstallStepper({ setServer }) {
           color="success"
           onClick={tryToConnect}
           startDecorator={<PlayIcon />}
-          sx={{ width: '100%', mt: 2 }}
-          disabled={activeStep !== 5}
+          sx={{ width: '100%', mt: 2, flex: 1, display: 'flex' }}
+          disabled={activeStep !== Steps.length}
         >
           Connect
         </Button>
       }
-    </>
+    </Sheet>
   );
 }
 
 function LocalConnection({ setServer }) {
-  return (
-    <Sheet sx={{ overflowY: 'auto' }}>
-      {/* {serverError
-        ? `Server is not running` + serverError && serverError.status
-        : 'Server is connected'} */}
-      <InstallStepper setServer={setServer} />
-    </Sheet>
-  );
+  return <InstallStepper setServer={setServer} />;
 }
 
 export default LocalConnection;
diff --git a/src/renderer/components/Connect/LoginModal.tsx b/src/renderer/components/Connect/LoginModal.tsx
index 3b41a075..f5d53e99 100644
--- a/src/renderer/components/Connect/LoginModal.tsx
+++ b/src/renderer/components/Connect/LoginModal.tsx
@@ -96,12 +96,12 @@ export default function LoginModal({
         aria-labelledby="basic-modal-dialog-title"
         aria-describedby="basic-modal-dialog-description"
         sx={{
-          top: '10vh', // Sit 20% from the top of the screen
+          top: '5vh', // Sit 20% from the top of the screen
           margin: 'auto',
           transform: 'translateX(-50%)', // This undoes the default translateY that centers vertically
           width: '55vw',
           maxWidth: '700px',
-          maxHeight: '70vh',
+          maxHeight: '90vh',
         }}
       >
         <Tabs
@@ -115,7 +115,15 @@ export default function LoginModal({
             <Tab>Remote Connection</Tab>
             {/* <Tab value="SSH">Connect via SSH</Tab> */}
           </TabList>
-          <TabPanel value={0} sx={{ p: 2, overflowY: 'auto' }}>
+          <TabPanel
+            value={0}
+            sx={{
+              p: 2,
+              overflowY: 'hidden',
+              display: 'flex',
+              flexDirection: 'column',
+            }}
+          >
             <LocalConnection setServer={setServer} />
           </TabPanel>
           <TabPanel value={1} sx={{ p: 2 }}>
-- 
GitLab