Skip to content

Commit

Permalink
Add deployments support
Browse files Browse the repository at this point in the history
  • Loading branch information
franalgaba committed Dec 22, 2023
1 parent 784de00 commit 32fc89b
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 3 deletions.
29 changes: 29 additions & 0 deletions giza/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import typer.rich_utils
from rich.traceback import install

from giza.commands.deployments import app as deployments_app
from giza.commands.deployments import deploy
from giza.commands.models import app as models_app
from giza.commands.prove import prove
from giza.commands.reset_password import request_reset_password_token, reset_password
Expand All @@ -29,6 +31,13 @@
help="""💻 Utilities for managing models""",
)

app.add_typer(
deployments_app,
name="deployments",
short_help="🚀 Utilities for managing deployments",
help="""🚀 Utilities for managing deployments""",
)

app.callback(
name="giza",
help="""
Expand Down Expand Up @@ -68,6 +77,26 @@
""",
)(transpile)

app.command(
short_help="🚀 Creates a deployment for the specified model version. Shortcut for `giza deployments deploy`",
help="""🚀 Creates a deployment for the specified model version. Shortcut for `giza deployments deploy`.
This command has different behavior depending on the framework:
* For Cairo, it will create an inference endpoint of the deployment in Giza.
* For EZKL, not implemented yet.
This command performs several operations:
* Creates a deployment for the specified version
* Uploads the specified version file
* Polls the version until the status is either FAILED or COMPLETED
* If the status is COMPLETED, sends back the deployment url to make inference requests
Error handling is also incorporated into this process.
""",
)(deploy)

app.command(
name="prove",
short_help="🔒 Command to generate a proof",
Expand Down
112 changes: 112 additions & 0 deletions giza/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from rich import print, print_json

from giza.schemas import users
from giza.schemas.deployments import Deployment, DeploymentCreate, DeploymentsList
from giza.schemas.jobs import Job, JobCreate
from giza.schemas.message import Msg
from giza.schemas.models import Model, ModelCreate, ModelList, ModelUpdate
Expand Down Expand Up @@ -430,6 +431,117 @@ def reset_password(self, token: str, new_password: str) -> Msg:
raise Exception("Could not reset the password")


class DeploymentsClient(ApiClient):
"""
Client to interact with `deployments` endpoint.
"""

DEPLOYMENTS_ENDPOINT = "deployments"
MODELS_ENDPOINT = "models"
VERSIONS_ENDPOINT = "versions"

@auth
def create(
self,
model_id: int,
version_id: int,
deployment_create: DeploymentCreate,
f: BufferedReader,
) -> Deployment:
"""
Create a new deployment.
Args:
deployment_create: Deployment information to create
Returns:
The recently created deployment information
"""
headers = copy.deepcopy(self.default_headers)
headers.update(self._get_auth_header())

response = self.session.post(
os.path.join(
self.url,
self.MODELS_ENDPOINT,
str(model_id),
self.VERSIONS_ENDPOINT,
str(version_id),
self.DEPLOYMENTS_ENDPOINT,
),
headers=headers,
params=deployment_create.dict(),
files={"casm": f} if f is not None else None,
)
self._echo_debug(str(response))

response.raise_for_status()

return Deployment(**response.json())

@auth
def list(self, model_id: int, version_id: int) -> List[Deployment]:
"""
List deployments.
Returns:
A list of deployments created by the user
"""
headers = copy.deepcopy(self.default_headers)
headers.update(self._get_auth_header())

response = self.session.get(
os.path.join(
self.url,
self.MODELS_ENDPOINT,
str(model_id),
self.VERSIONS_ENDPOINT,
str(version_id),
self.DEPLOYMENTS_ENDPOINT,
),
headers=headers,
)
self._echo_debug(str(response))

response.raise_for_status()

return DeploymentsList(
__root__=[Deployment(**deployment) for deployment in response.json()]
)

@auth
def get(self, model_id: int, version_id: int, deployment_id: int) -> Deployment:
"""
Get a deployment.
Args:
deployment_id: Deployment identifier
Returns:
The deployment information
"""
headers = copy.deepcopy(self.default_headers)
headers.update(self._get_auth_header())

response = self.session.get(
os.path.join(
self.url,
self.MODELS_ENDPOINT,
str(model_id),
self.VERSIONS_ENDPOINT,
str(version_id),
self.DEPLOYMENTS_ENDPOINT,
str(deployment_id),
),
headers=headers,
)

self._echo_debug(str(response))
response.raise_for_status()

return Deployment(**response.json())


class TranspileClient(ApiClient):
"""
Client to interact with `users` endpoint.
Expand Down
144 changes: 144 additions & 0 deletions giza/commands/deployments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import sys
from typing import Optional

import typer
from pydantic import ValidationError
from requests import HTTPError
from rich import print_json

from giza import API_HOST
from giza.client import DeploymentsClient
from giza.frameworks import cairo
from giza.options import DEBUG_OPTION
from giza.schemas.deployments import DeploymentsList
from giza.utils import echo, get_response_info
from giza.utils.enums import Framework, ServiceSize

app = typer.Typer()


def deploy(
data: str = typer.Argument(None),
model_id: int = typer.Option(
None, help="The ID of the model where a deployment will be created"
),
version_id: int = typer.Option(
None, help="The ID of the version that will be deployed"
),
size: ServiceSize = typer.Option(ServiceSize.S, "--size", "-s"),
framework: Framework = typer.Option(Framework.CAIRO, "--framework", "-f"),
debug: Optional[bool] = DEBUG_OPTION,
) -> None:
if framework == Framework.CAIRO:
cairo.deploy(
data=data, model_id=model_id, version_id=version_id, size=size, debug=debug
)
elif framework == Framework.EZKL:
raise NotImplementedError(
"EZKL deployment is not yet supported, please use Cairo instead"
)
else:
raise typer.BadParameter(
f"Framework {framework} is not supported, please use one of the following: {Framework.CAIRO}, {Framework.EZKL}"
)


app.command(
short_help="🚀 Creates a deployment for the specified model version.",
help="""🚀 Creates a deployment for the specified model version.
This command has different behavior depending on the framework:
* For Cairo, it will create an inference endpoint of the deployment in Giza.
* For EZKL, not implemented yet.
This command performs several operations:
* Creates a deployment for the specified version
* Uploads the specified version file
* Polls the version until the status is either FAILED or COMPLETED
* If the status is COMPLETED, sends back the deployment url to make inference requests
Error handling is also incorporated into this process.
""",
)(deploy)


@app.command(
short_help="📜 List the available deployments.",
help="""📜 Lists all the available deployments in Giza.
This command retrieves and displays a list of all deployments stored in the server.
Each deployment's information is printed in a json format for easy readability and further processing.
If there are no deployments available, an empty list is printed.
""",
)
def list(
model_id: int = typer.Option(None, help="The ID of the model"),
version_id: int = typer.Option(None, help="The ID of the version"),
debug: Optional[bool] = DEBUG_OPTION,
) -> None:
echo("Listing deployments ✅ ")
try:
client = DeploymentsClient(API_HOST)
deployments: DeploymentsList = client.list(model_id, version_id)
except ValidationError as e:
echo.error("Deployment validation error")
echo.error("Review the provided information")
if debug:
raise e
echo.error(str(e))
sys.exit(1)
except HTTPError as e:
info = get_response_info(e.response)
echo.error("⛔️Could not list deployments")
echo.error(f"⛔️Detail -> {info.get('detail')}⛔️")
echo.error(f"⛔️Status code -> {info.get('status_code')}⛔️")
echo.error(f"⛔️Error message -> {info.get('content')}⛔️")
echo.error(
f"⛔️Request ID: Give this to an administrator to trace the error -> {info.get('request_id')}⛔️"
) if info.get("request_id") else None
if debug:
raise e
sys.exit(1)
print_json(deployments.json())


# giza/commands/deployments.py
@app.command(
short_help="🎯 Get a deployment.",
help="""🎯 Get a specific deployment in Giza.
This command retrieves and displays a specific deployment stored in the server.
The deployment's information is printed in a json format for easy readability and further processing.
If the deployment is not available, an error message is printed.
""",
)
def get(
model_id: int = typer.Option(None, help="The ID of the model"),
version_id: int = typer.Option(None, help="The ID of the version"),
deployment_id: int = typer.Option(None, help="The ID of the version"),
debug: Optional[bool] = DEBUG_OPTION,
) -> None:
echo(f"Getting deployment {deployment_id} ✅ ")
try:
client = DeploymentsClient(API_HOST)
deployment = client.get(model_id, version_id, deployment_id)
except ValidationError as e:
echo.error("Deployment validation error")
echo.error("Review the provided information")
if debug:
raise e
echo.error(str(e))
sys.exit(1)
except HTTPError as e:
info = get_response_info(e.response)
echo.error(f"⛔️Could not get deployment {deployment_id}")
echo.error(f"⛔️Detail -> {info.get('detail')}⛔️")
echo.error(f"⛔️Status code -> {info.get('status_code')}⛔️")
echo.error(f"⛔️Error message -> {info.get('content')}⛔️")
echo.error(
f"⛔️Request ID: Give this to an administrator to trace the error -> {info.get('request_id')}⛔️"
) if info.get("request_id") else None
if debug:
raise e
sys.exit(1)
print_json(deployment.json())
Loading

0 comments on commit 32fc89b

Please sign in to comment.