Skip to content

Commit

Permalink
[CLI]
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Oct 9, 2024
1 parent 2706a5b commit b684c12
Show file tree
Hide file tree
Showing 8 changed files with 435 additions and 9 deletions.
34 changes: 27 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "swarms-cloud"
version = "0.3.7"
version = "0.3.8"
description = "Swarms Cloud - Pytorch"
license = "MIT"
authors = ["Kye Gomez <[email protected]>"]
Expand All @@ -23,17 +23,37 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.10"
swarms = "*"
fastapi = "*"
skypilot = "*"
fastapi = "0.112.2"
supabase = "*"
pytest = "*"
torch = "*"
einops = "*"
pydantic = ">2,<3"
stripe = "*"
transformers = "*"
sse-starlette = "2.1.3"
tiktoken = "*"
sse-starlette = "2.0.0"
uvicorn = "*"
loguru = "*"
pydantic = "*"
stripe = "*"
xformers = "*"
diffusers = "*"
transformers_stream_generator = "*"
bitsandbytes = "*"
peft = "*"
accelerate = "*"
transformers = "4.44.0"
huggingface-hub = "*"
optimum = "*"
auto-gptq = "*"
whisperx = "*"
shortuuid = "*"
hf_transfer = "*"
swarms = "*"
rich = "*"
asyncio = "*"

[tool.poetry.scripts]
swarms-cloud = "swarms_cloud.cli.main:main"

[tool.poetry.group.lint.dependencies]
ruff = ">=0.1.6,<0.7.0"
Expand Down
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ uvicorn
tiktoken
pydantic
asyncio
swarms-cloud
swarms-cloud
rich
tiktoken
shortuuid
3 changes: 3 additions & 0 deletions swarms_cloud/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from swarms_cloud.cli.onboarding import OnboardingProcess

__all__ = ["OnboardingProcess"]
108 changes: 108 additions & 0 deletions swarms_cloud/cli/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import argparse
import os
import time

from rich.console import Console
from rich.text import Text

from swarms_cloud.cli.onboarding import OnboardingProcess

console = Console()


ASCII_ART = """
_________
/ _____/_ _ _______ _______ _____ ______
\_____ \\ \/ \/ /\__ \\_ __ \/ \ / ___/
/ \\ / / __ \| | \/ Y Y \\___ \
/_______ / \/\_/ (____ /__| |__|_| /____ >
\/ \/ \/ \/
"""


# Function to display the ASCII art in red
def show_ascii_art():
text = Text(ASCII_ART, style="bold red")
console.print(text)


# Help command
def show_help():
console.print(
"""
[bold cyan]Swarms Cloud CLI - Help[/bold cyan]
[bold magenta]Commands:[/bold magenta]
[bold white]onboarding[/bold white] : Starts the onboarding process
[bold white]help[/bold white] : Shows this help message
[bold white]get-api-key[/bold white] : Retrieves your API key from the platform
[bold white]check-login[/bold white] : Checks if you're logged in and starts the cache
For more details, visit: https://docs.swarms.world
"""
)


# Fetch API key from platform
def get_api_key():
console.print("[bold yellow]Opening the API key retrieval page...[/bold yellow]")
# Simulating API key retrieval process by opening the website
import webbrowser

webbrowser.open("https://swarms.world/platform/api-keys")
time.sleep(2)
console.print(
"[bold green]Your API key is available on the dashboard.[/bold green]"
)


# Check and start cache (login system simulation)
def check_login():
cache_file = "cache.txt"

if os.path.exists(cache_file):
with open(cache_file, "r") as f:
cache_content = f.read()
if cache_content == "logged_in":
console.print("[bold green]You are already logged in.[/bold green]")
else:
console.print("[bold red]You are not logged in.[/bold red]")
else:
console.print("[bold yellow]Logging in...[/bold yellow]")
time.sleep(2)
with open(cache_file, "w") as f:
f.write("logged_in")
console.print("[bold green]Login successful![/bold green]")


# Main CLI handler
def main():
parser = argparse.ArgumentParser(description="Swarms Cloud CLI")

# Adding arguments for different commands
parser.add_argument(
"command",
choices=["onboarding", "help", "get-api-key", "check-login"],
help="Command to run",
)

args = parser.parse_args()

show_ascii_art()

# Determine which command to run
if args.command == "onboarding":
OnboardingProcess().run()
elif args.command == "help":
show_help()
elif args.command == "get-api-key":
get_api_key()
elif args.command == "check-login":
check_login()
else:
console.print("[bold red]Unknown command! Type 'help' for usage.[/bold red]")


if __name__ == "__main__":
main()
197 changes: 197 additions & 0 deletions swarms_cloud/cli/onboarding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import platform
import os
import socket
import psutil
import uuid
from loguru import logger
import json
import time
from typing import Dict
import requests
from swarms_cloud.utils.log_to_swarms_database import log_agent_data


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] = self.capture_system_data()
self.auto_save_path = auto_save_path
self.cache_save_path = cache_save_path
self.load_existing_data()

def capture_system_data(self) -> Dict[str, str]:
"""
Captures extensive system data including platform information, user ID, IP address, CPU count,
memory information, and other system details.
Returns:
Dict[str, str]: A dictionary containing system data.
"""
try:
system_data = {
"platform": platform.system(),
"platform_version": platform.version(),
"platform_release": platform.release(),
"hostname": socket.gethostname(),
"ip_address": socket.gethostbyname(socket.gethostname()),
"cpu_count": psutil.cpu_count(logical=True),
"memory_total": f"{psutil.virtual_memory().total / (1024 ** 3):.2f} GB",
"memory_available": f"{psutil.virtual_memory().available / (1024 ** 3):.2f} GB",
"user_id": str(uuid.uuid4()), # Unique user identifier
"machine_type": platform.machine(),
"processor": platform.processor(),
"architecture": platform.architecture()[0],
}

# Get external IP address
try:
system_data["external_ip"] = requests.get("https://api.ipify.org").text
except Exception as e:
logger.warning("Failed to retrieve external IP: {}", e)
system_data["external_ip"] = "N/A"

return system_data
except Exception as e:
logger.error("Failed to capture system data: {}", e)
return {}

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)
# threading.Thread(target=log_agent_data(combined_data)).start()
with open(self.auto_save_path, "w") as f:
json.dump(combined_data, f, indent=4)
# logger.info(
# "User and system data successfully saved to {}",
# self.auto_save_path,
# )
with open(self.cache_save_path, "w") as f:
json.dump(combined_data, f, indent=4)
# logger.info(
# "User and system data successfully cached in {}",
# self.cache_save_path,
# )
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()
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.
"""
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")
self.ask_input(
"Enter your Swarms API key (or type 'quit' to exit): Get this in your swarms dashboard: https://swarms.world/platform/api-keys ",
"swarms_api_key",
)
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()
2 changes: 1 addition & 1 deletion swarms_cloud/structs/deploy_gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pulumi
import yaml
from loguru import logger
from pulumi_gcp import cloudrun, storage
from pulumi_gcp import cloudrun

# Configure logging
logger.add("deploy_agent.log", rotation="10 MB", retention="10 days", level="INFO")
Expand Down
Loading

0 comments on commit b684c12

Please sign in to comment.