Skip to content

Commit

Permalink
feat: Add api, Deploy command and update cli
Browse files Browse the repository at this point in the history
  • Loading branch information
pythonbyte committed Aug 21, 2024
1 parent bec9a49 commit f44d390
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 55 deletions.
7 changes: 5 additions & 2 deletions src/crewai/cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import click
import pkg_resources

Expand Down Expand Up @@ -183,9 +185,10 @@ def deploy():


@deploy.command(name="up")
def deploy_up():
@click.option("-u", "--uuid", type=Optional[str], help="Crew UUID parameter")
def deploy_up(uuid: Optional[str]):
"""Deploy the crew."""
deploy_cmd.deploy()
deploy_cmd.deploy(uuid=uuid)


@deploy.command(name="create")
Expand Down
62 changes: 62 additions & 0 deletions src/crewai/cli/deploy/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from os import getenv

import requests


class CrewAPI:
"""
CrewAPI class to interact with the crewAI+ API.
"""

CREW_BASE_URL = getenv("BASE_URL", "http://localhost:3000/crewai_plus/api/v1/crews")
MAIN_BASE_URL = getenv("MAIN_BASE_URL", "http://localhost:3000/crewai_plus/api/v1")

def __init__(self, api_key: str) -> None:
self.api_key = api_key
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}

def _make_request(
self, method: str, endpoint: str, base_url: str = CREW_BASE_URL, **kwargs
) -> requests.Response:
url = f"{base_url}/{endpoint}"
return requests.request(method, url, headers=self.headers, **kwargs)

def deploy_by_name(self, project_name: str) -> requests.Response:
return self._make_request("POST", f"by-name/{project_name}/deploy")

def deploy_by_uuid(self, uuid: str) -> requests.Response:
return self._make_request("POST", f"{uuid}/deploy")

def status_by_name(self, project_name: str) -> requests.Response:
return self._make_request("GET", f"by-name/{project_name}/status")

def status_by_uuid(self, uuid: str) -> requests.Response:
return self._make_request("GET", f"{uuid}/status")

def logs_by_name(
self, project_name: str, log_type: str = "deployment"
) -> requests.Response:
return self._make_request("GET", f"by-name/{project_name}/logs/{log_type}")

def logs_by_uuid(
self, uuid: str, log_type: str = "deployment"
) -> requests.Response:
return self._make_request("GET", f"{uuid}/logs/{log_type}")

def delete_by_name(self, project_name: str) -> requests.Response:
return self._make_request("DELETE", f"by-name/{project_name}")

def delete_by_uuid(self, uuid: str) -> requests.Response:
return self._make_request("DELETE", f"{uuid}")

def list_crews(self) -> requests.Response:
return self._make_request("GET", "")

def create_crew(self, payload) -> requests.Response:
return self._make_request("POST", "", json=payload)

def signup(self) -> requests.Response:
return self._make_request("GET", "signup_link", base_url=self.MAIN_BASE_URL)
167 changes: 115 additions & 52 deletions src/crewai/cli/deploy/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from os import getenv
from typing import Optional

import requests
from rich.console import Console

from .api import CrewAPI
from .utils import (
fetch_and_json_env_file,
get_auth_token,
Expand All @@ -18,76 +19,130 @@ class DeployCommand:

def __init__(self):
self.project_name = get_project_name()
self.remote_repo_url = get_git_remote_url()

def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
url = f"{self.BASE_URL}/{endpoint}"
headers = {
"Authorization": f"Bearer {get_auth_token()}",
"Content-Type": "application/json",
}
return requests.request(method, url, headers=headers, **kwargs)
self.client = CrewAPI(api_key=get_auth_token())

def _handle_error(self, json_response: dict) -> None:
error = json_response.get("error")
message = json_response.get("message")
console.print(
f"Error: {error}",
style="bold red",
)
console.print(
f"Message: {message}",
style="bold red",
)

def deploy(self) -> None:
console.print("Deploying the crew...", style="bold blue")
response = self._make_request(
"POST", f"crews/by-name/{self.project_name}/deploy"
def _standard_no_param_error_message(self) -> None:
console.print(
"No uuid provided, project pyproject.toml not found or with error.",
style="bold red",
)
console.print(response.json())

def deploy(self, uuid: Optional[str] = None) -> None:
console.print("Starting deployment...", style="bold blue")
if uuid:
response = self.client.deploy_by_uuid(uuid)
elif self.project_name:
response = self.client.deploy_by_name(self.project_name)
else:
self._standard_no_param_error_message()
return

json_response = response.json()
if response.status_code == 200:
console.print("Deploying the crew...\n", style="bold blue")

for key, value in json_response.items():
console.print(f"{key.title()}: [green]{value}[/green]")

console.print("\nTo check the status of the deployment, run:")
console.print("crewai deploy status")
console.print(" or")
console.print(f"crewai deploy status --uuid \"{json_response['uuid']}\"")

else:
self._handle_error(json_response)

def create_crew(self) -> None:
console.print("Creating deployment...", style="bold blue")
env_vars = fetch_and_json_env_file()
remote_repo_url = get_git_remote_url()

input(f"Press Enter to continue with the following Env vars: {env_vars}")
input(
f"Press Enter to continue with the following remote repository: {remote_repo_url}\n"
)
payload = {
"deploy": {
"name": self.project_name,
"repo_clone_url": self.remote_repo_url,
"repo_clone_url": remote_repo_url,
"env": env_vars,
}
}
response = self._make_request("POST", "crews", json=payload)
console.print(response.json())

response = self.client.create_crew(payload)
if response.status_code == 201:
json_response = response.json()
console.print("Deployment created successfully!\n", style="bold green")
console.print(
f"Name: {self.project_name} ({json_response['uuid']})",
style="bold green",
)
console.print(f"Status: {json_response['status']}", style="bold green")
console.print("\nTo (re)deploy the crew, run:")
console.print("crewai deploy up")
console.print(" or")
console.print(f"crewai deploy --uuid {json_response['uuid']}")
else:
self._handle_error(response.json())

def list_crews(self) -> None:
console.print("Listing all Crews", style="bold blue")
response = self._make_request("GET", "crews")
crews_data = response.json()
console.print("Listing all Crews\n", style="bold blue")

response = self.client.list_crews()
json_response = response.json()
if response.status_code == 200:
if crews_data:
for crew_data in crews_data:
console.print(
f"- {crew_data['name']} ({crew_data['uuid']}) [blue]{crew_data['status']}[/blue]"
)
else:
for crew_data in json_response:
console.print(
"You don't have any crews yet. Let's create one!", style="yellow"
f"- {crew_data['name']} ({crew_data['uuid']}) [blue]{crew_data['status']}[/blue]"
)
console.print("\t[green]crewai create --name [name][/green]")

def get_crew_status(self) -> None:
console.print("Getting deployment status...", style="bold blue")
response = self._make_request(
"GET", f"crews/by-name/{self.project_name}/status"
)

if response.status_code == 200:
status_data = response.json()
console.print(f"Name:\t {status_data['name']}")
console.print(f"Status:\t {status_data['status']}")
console.print("\nUsage:")
console.print(f"\tcrewai inputs --name \"{status_data['name']}\"")
else:
console.print(
f"\tcrewai kickoff --name \"{status_data['name']}\" --inputs [INPUTS]"
"You don't have any crews yet. Let's create one!", style="yellow"
)
console.print(" [green]crewai create --name [name][/green]")

def get_crew_status(self, uuid: Optional[str] = None) -> None:
console.print("Fetching deployment status...", style="bold blue")
if uuid:
response = self.client.status_by_uuid(uuid)
elif self.project_name:
response = self.client.status_by_name(self.project_name)
else:
console.print(response.json(), style="bold red")
self._standard_no_param_error_message()
return

def get_crew_logs(self, log_type: str = "deployment") -> None:
console.print("Getting deployment logs...", style="bold blue")
response = self._make_request(
"GET", f"crews/by-name/{self.project_name}/logs/{log_type}"
)
json_response = response.json()
if response.status_code == 200:
console.print(f"Name:\t {json_response['name']}")
console.print(f"Status:\t {json_response['status']}")

else:
self._handle_error(json_response)

def get_crew_logs(
self, uuid: Optional[str], log_type: str = "dExacployment"
) -> None:
console.print(f"Getting {log_type} logs...", style="bold blue")

if uuid:
response = self.client.logs_by_uuid(uuid, log_type)
elif self.project_name:
response = self.client.logs_by_name(self.project_name, log_type)
else:
self._standard_no_param_error_message()
return

if response.status_code == 200:
log_messages = response.json()
Expand All @@ -98,9 +153,16 @@ def get_crew_logs(self, log_type: str = "deployment") -> None:
else:
console.print(response.text, style="bold red")

def remove_crew(self) -> None:
def remove_crew(self, uuid: Optional[str]) -> None:
console.print("Removing deployment...", style="bold blue")
response = self._make_request("DELETE", f"crews/by-name/{self.project_name}")

if uuid:
response = self.client.delete_by_uuid(uuid)
elif self.project_name:
response = self.client.delete_by_name(self.project_name)
else:
self._standard_no_param_error_message()
return

if response.status_code == 204:
console.print(
Expand All @@ -113,8 +175,9 @@ def remove_crew(self) -> None:

def signup(self) -> None:
console.print("Signing Up", style="bold blue")
response = self._make_request("GET", "signup_link")
response = self.client.signup()

# signup_command(response["signup_link"])
if response.status_code == 200:
data = response.json()
console.print(f"Temporary credentials: {data['token']}")
Expand Down
8 changes: 7 additions & 1 deletion src/crewai/cli/deploy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,19 @@ def get_project_name(pyproject_path: str = "pyproject.toml"):
# Extract the project name
project_name = pyproject_content["tool"]["poetry"]["name"]

if "crewai" not in pyproject_content["tool"]["poetry"]["dependencies"]:
raise Exception("crewai is not in the dependencies.")

return project_name

except FileNotFoundError:
print(f"Error: {pyproject_path} not found.")
except KeyError:
print("Error: 'name' not found in [tool.poetry] section.")
print(f"Error: {pyproject_path} is not a valid pyproject.toml file.")
except tomllib.TOMLDecodeError:
print(f"Error: {pyproject_path} is not a valid TOML file.")
except Exception as e:
print(f"Error reading the pyproject.toml file: {e}")

return None

Expand Down

0 comments on commit f44d390

Please sign in to comment.