File size: 6,952 Bytes
ffcf62f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import json
import os
import time
from typing import Dict

from swarms.utils.loguru_logger import initialize_logger


from swarms.telemetry.capture_sys_data import (
    capture_system_data,
    log_agent_data,
)

logger = initialize_logger(log_folder="onboarding_process")


class OnboardingProcess:
    """
    This class handles the onboarding process for users. It collects user data including their
    full name, first name, email, Swarms API key, and system data, then autosaves it in both a
    main JSON file and a cache file for reliability. It supports loading previously saved or cached data.
    """

    def __init__(
        self,
        auto_save_path: str = "user_data.json",
        cache_save_path: str = "user_data_cache.json",
    ) -> None:
        """
        Initializes the OnboardingProcess with an autosave file path and a cache path.

        Args:
            auto_save_path (str): The path where user data is automatically saved.
            cache_save_path (str): The path where user data is cached for reliability.
        """
        self.user_data: Dict[str, str] = {}
        self.system_data: Dict[str, str] = capture_system_data()
        self.auto_save_path = auto_save_path
        self.cache_save_path = cache_save_path
        self.load_existing_data()

    def load_existing_data(self) -> None:
        """
        Loads existing user data from the auto-save file or cache if available.
        """
        if os.path.exists(self.auto_save_path):
            try:
                with open(self.auto_save_path, "r") as f:
                    self.user_data = json.load(f)
                    logger.info(
                        "Existing user data loaded from {}",
                        self.auto_save_path,
                    )
                    return
            except json.JSONDecodeError as e:
                logger.error(
                    "Failed to load user data from main file: {}", e
                )

        # Fallback to cache if main file fails
        if os.path.exists(self.cache_save_path):
            try:
                with open(self.cache_save_path, "r") as f:
                    self.user_data = json.load(f)
                    logger.info(
                        "User data loaded from cache: {}",
                        self.cache_save_path,
                    )
            except json.JSONDecodeError as e:
                logger.error(
                    "Failed to load user data from cache: {}", e
                )

    def save_data(self, retry_attempts: int = 3) -> None:
        """
        Saves the current user data to both the auto-save file and the cache file. If the main
        save fails, the cache is updated instead. Implements retry logic with exponential backoff
        in case both save attempts fail.

        Args:
            retry_attempts (int): The number of retries if saving fails.
        """
        attempt = 0
        backoff_time = 1  # Starting backoff time (in seconds)

        while attempt < retry_attempts:
            try:
                combined_data = {**self.user_data, **self.system_data}
                log_agent_data(combined_data)
                return  # Exit the function if saving was successful
            except Exception as e:
                logger.error(
                    "Error saving user data (Attempt {}): {}",
                    attempt + 1,
                    e,
                )

            # Retry after a short delay (exponential backoff)
            time.sleep(backoff_time)
            attempt += 1
            backoff_time *= (
                2  # Double the backoff time for each retry
            )

        logger.error(
            "Failed to save user data after {} attempts.",
            retry_attempts,
        )

    def ask_input(self, prompt: str, key: str) -> None:
        """
        Asks the user for input, validates it, and saves it in the user_data dictionary.
        Autosaves and caches after each valid input.

        Args:
            prompt (str): The prompt message to display to the user.
            key (str): The key under which the input will be saved in user_data.

        Raises:
            ValueError: If the input is empty or only contains whitespace.
        """
        try:
            response = input(prompt)
            if response.strip().lower() == "quit":
                logger.info(
                    "User chose to quit the onboarding process."
                )
                exit(0)
            if not response.strip():
                raise ValueError(
                    f"{key.capitalize()} cannot be empty."
                )
            self.user_data[key] = response.strip()
            self.save_data()

            return response
        except ValueError as e:
            logger.warning(e)
            self.ask_input(prompt, key)
        except KeyboardInterrupt:
            logger.warning(
                "Onboarding process interrupted by the user."
            )
            exit(1)

    def collect_user_info(self) -> None:
        """
        Initiates the onboarding process by collecting the user's full name, first name, email,
        Swarms API key, and system data. Additionally, it reminds the user to set their WORKSPACE_DIR environment variable.
        """
        logger.info("Initiating swarms cloud onboarding process...")
        self.ask_input(
            "Enter your first name (or type 'quit' to exit): ",
            "first_name",
        )
        self.ask_input(
            "Enter your Last Name (or type 'quit' to exit): ",
            "last_name",
        )
        self.ask_input(
            "Enter your email (or type 'quit' to exit): ", "email"
        )
        workspace = self.ask_input(
            "Enter your WORKSPACE_DIR: This is where logs, errors, and agent configurations will be stored (or type 'quit' to exit). Remember to set this as an environment variable: https://docs.swarms.world/en/latest/swarms/install/quickstart/ || ",
            "workspace_dir",
        )
        os.environ["WORKSPACE_DIR"] = workspace
        logger.info(
            "Important: Please ensure you have set your WORKSPACE_DIR environment variable as per the instructions provided."
        )
        logger.info(
            "Additionally, remember to add your API keys for your respective models in your .env file."
        )
        logger.success("Onboarding process completed successfully!")

    def run(self) -> None:
        """
        Main method to run the onboarding process. It handles unexpected errors and ensures
        proper finalization.
        """
        try:
            self.collect_user_info()
        except Exception as e:
            logger.error("An unexpected error occurred: {}", e)
        finally:
            logger.info("Finalizing the onboarding process.")


# if __name__ == "__main__":
#     onboarding = OnboardingProcess()
#     onboarding.run()