Skip to main content

Python client for Outpost

Project description

Outpost Kit Python client

This is a Python client for Outpost. It lets you run models from your Python code or Outpost Notebook, and do various other things on Outpost.

Requirements

  • Python 3.8+

Install

pip install outpostkit

Authenticate

Before running any Python scripts that use the API, you need to set your Outpost API token in your environment.

Get your token from outpost.run/ and set it as an environment variable:

export OUTPOST_API_TOKEN=<your token>

Check the version

import outpostkit
print(outpostkit.__version__)

0.0.69

Create a client

from outpostkit import Client
import os
client = Client(api_token=os.environ.get('OUTPOST_API_TOKEN'))

Get details of the authenticated user

print(client.user)
UserDetails(id='lqiuxjj6okbzt72j1eyk5vn2', createdAt='2023-12-08T13:26:01.277Z', updatedAt='2024-01-08T12:17:45.927Z', stats=UserStats(followers_count=1, following_count=2), avatarUrl='https://avatars.outpost.run/e/lqiuxjj6okbzt72j1eyk5vn2', name='aj-ya', bio=None, socials=None, displayName='Ajeya Bhat')

Create an Endpoint

The easiest way to create an endpoint is by providing just the model and let us handle the server specifics. We use the model's task type, library and other details to generate the template that serves it.

from outpostkit import Endpoints
from outpostkit._types.endpoints import EndpointAutogeneratedTemplateConfig, EndpointAutogeneratedHFModelDetails

client = Client(api_token=os.environ.get('OUTPOST_API_TOKEN'))
endpoints_client = Endpoints(client=client, entity=client.user.name)

template = EndpointAutogeneratedTemplateConfig(modelSource="huggingface",huggingfaceModel=EndpointAutogeneratedHFModelDetails(id="Falconsai/text_summarization"))  
endpoint = endpoints_client.create(template=template)

Deploy the endpoint

once you create the endpoint, you need to deploy it.

endpoint.deploy()

To reflect the updates in configurations of the endpoint, it must be redeployed.

Once it is deployed... a dummy request is sent to the prediction path to trigger the scale up. Thus it is available for atleast for the duration of the scaledownPeriod. To not wake up the deployment. you can use set it the deployment parameters.

endpoint.deploy(wakeup=False)

Get prediction from the endpoint

Once the endpoint is available, you can test the predictions over HTTP.

The requests to the prediction path must be authenticated with outpost access token.

You can also use our prediction client for that. (which uses our main client for the token.)

pred_client = endpoint.get_prediction_client()
resp = pred_client.infer(json={
    "documents":"""Imagine you are standing in the middle of a room with no windows, doors or lights. What do you see? Well, nothing because there’s no light. Now imagine you pull out a flashlight and turn it on. The light from the flashlight moves in a straight line. When that beam of light hits an object, the light bounces off that item and into your eyes, allowing you to see whatever is inside the room.
    All light behaves just like that flashlight — it travels in a straight line. But, light also bounces off of objects, which is what allows us to see and photograph objects. When light bounces off an object, it continues to travel in a straight line, but it bounces back at the same angle that it comes in at. That means light rays are essentially bouncing everywhere in all kinds of different directions. The first camera was essentially a room with a small hole on one side wall. Light would pass through that hole, and since it’s reflected in straight lines, the image would be projected on the opposite wall, upside down. While devices like this existed long before true photography, it wasn’t until someone decided to place material that was sensitive to light at the back of that room that photography was born. When light hit the material, which through the course of photography’s history was made up of things from glass to paper, the chemicals reacted to light, etching an image in the surface."""
})

print(resp.json())
[
    {
        "summary_text": "Imagine you are standing in the middle of a room with no windows, doors or lights . When that beam of light hits an object, the light bounces off that item and into your eyes, allowing you to see whatever is inside the room . All light behaves just like that flashlight — it travels in a straight line, but it bounces back at the same angle that it comes in at . That means light rays are essentially bouncing everywhere in all kinds of different directions ."
    }
]

Development

See CONTRIBUTING.md

Endpoint Creation Payload

    def create(
        self,
        template: Union[
            EndpointAutogeneratedTemplateConfig, EndpointCustomTemplateConfig
        ],
        container: Optional[EndpointPrebuiltContainerDetails] = None,
        hardware_instance: str = "e2-standard-2",
        task_type: Optional[str] = None,
        name: Optional[str] = None,
        secrets: Optional[List[EndpointSecret]] = None,
        visibility: ServiceVisibility = ServiceVisibility.public,
        replica_scaling_config: Optional[ReplicaScalingConfig] = None,
    ) -> Endpoint: ...

An endpoint server needs to know certain things like model loading, prediction request handling, exception handling, etc. and these things vary with each usecase. Thus the server follows a template which tells it how to behave.

Template configuration

You can either let us autogenerate templates based on the model information, or create a custom template yourself.

Configs to Autogenerate Template

Currently we can autogenerate templates models stored at 'Outpost' or 'Hugging Face'.

Task types supported --TODO: List all task types supported--

To Import a model stored at Outpost, you can directly use:

from outpostkit._types.endpoints import EndpointAutogeneratedTemplateConfig, EndpointAutogeneratedOutpostModelDetails

template = EndpointAutogeneratedTemplateConfig(modelSource="outpost",outpostModel=EndpointAutogeneratedOutpostModelDetails(id="aj-ya/text-gen"))

If you have a specific revision of the model that you want to deploy, provide the revision in the revision field.

template = EndpointAutogeneratedTemplateConfig(modelSource="outpost", revision="df5ef1a0e2d2579726d74b5d617b17c7049c5a89",outpostModel=EndpointAutogeneratedOutpostModelDetails(id="aj-ya/text-gen")) 

To import gated/private models from Hugging Face, you can add your Hugging Face key as a third party token and provide its id in the config.

from outpostkit._types.endpoints import EndpointAutogeneratedTemplateConfig, EndpointAutogeneratedHFModelDetails
hf_model = EndpointAutogeneratedHFModelDetails(id="Falconsai/text_summarization",keyId="<thirdPartyTokenId>")
template = EndpointAutogeneratedTemplateConfig(modelSource="huggingface",huggingfaceModel=hf_model) 

Create a custom template

A Template class needs to mainly define model initialization and prediction request handling. For demonstration purposes, lets create a template file for the openai/shap-e model.

First of all we need to create a class and the load the model at the initialization phase. Then, we need to define the request handler for the /predict route. this is done by defining the predict member function. This function itself acts as the handler, thus you can define any parameters that FastAPI supports to the function. Here we will use a pydantic class to validate the request json body and get generation arguements. (ref: https://fastapi.tiangolo.com/tutorial/body/) Finally, we would like to also stream the output GIF, for this we will use the StreamingResponse object by fastapi.

from io import BytesIO
from typing import List
from diffusers import ShapEPipeline
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from PIL.Image import Image


class Item(BaseModel):
    prompt: str
    frame_size: int = 256
    num_inference_steps: int = 64
    guidance_scale: float = 15


def pil_gif_resp(image: List[Image]) -> StreamingResponse:
    temp = BytesIO()
    image[0].save(
        temp,
        save_all=True,
        append_images=image[1:],
        optimize=False,
        duration=100,
        loop=0,
    )
    return StreamingResponse(temp, media_type="image/gif")


class ShapEHandler:
    pipeline: ShapEPipeline

    def __init__(self) -> None:
        ckpt_id = "openai/shap-e"
        self.pipeline = ShapEPipeline.from_pretrained(ckpt_id).to("cuda")

    def predict(self, item: Item):
        images = self.pipeline(
            item.prompt,
            guidance_scale=item.guidance_scale,
            num_inference_steps=item.num_inference_steps,
            frame_size=item.frame_size,
        ).images
        return pil_gif_resp(images[0])
Installing extra packages and modules

If your application needs a specific python package or system dependency that is not already installed in the container image (--TODO-- docs showing the list of prebuilt container images and the packages installed there.). Then, you can define these members in the Template class

class Template:
    # extra system dependencies required
    system_dependencies: List[str] = ['curl']

    # extra python packages required
    python_requirements: List[str] = ['gif==23.0']
    ...
Exception Handling

To define exception handling outside the prediction handler, you can extend the default expection handling done by the server like this:

from fastapi.responses import JSONResponse

async def generic_exception_handler(_, exc: Exception):
    return JSONResponse(
        json.dumps({"error": str(exc), "type": "unhandled_error"}),
        status_code=500,
    )

class Template:
    # define custom exception handlers for the fastapi app
    exception_handlers: Dict[Union[int, Type[Exception]], Callable] = dict({
        Exception: generic_exception_handler
    })
    ...

Container configuration

if youre already using a prebuilt template, most of the times, you wont need to define this. It is already selected based on the library and task type.

But you can manually configure this as well. Currently, you can only use any one of many prebuilt containers that are provided by outpost.

Namely, --TODO-- list all container images here.

To use the tensorflow image with pytorch loaded, with some extra configs, use:

from outpostkit import EndpointPrebuiltContainerDetails
container = EndpointPrebuiltContainerDetails(name="transformers-pt", configs = {torch_dtype:'float32'})

Scaling Configuration

The horizontal scaling configurations of the endpoint are based on the number of requests to the prediction request path. You can tweak the settings at creation too.

from outpostkit._types.endpoint import ReplicaScalingConfig
scaling_config = ReplicaScalingConfig(min=0,max=1,scaledownPeriod=900,targetPendingRequests=100) # Defaults
endpoint = endpoints_client.create(template=template,replica_scaling_config = scaling_config)

scaledownPeriod: The period to wait after the last reported active before scaling the resource back to 0. targetPendingRequests: This is the number of pending (or in-progress) requests that your application needs to have before it is scaled up. Conversely, if your application has below this number of pending requests, it will scaled down.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

outpostkit-0.0.69.tar.gz (39.4 kB view details)

Uploaded Source

Built Distribution

outpostkit-0.0.69-py3-none-any.whl (43.6 kB view details)

Uploaded Python 3

File details

Details for the file outpostkit-0.0.69.tar.gz.

File metadata

  • Download URL: outpostkit-0.0.69.tar.gz
  • Upload date:
  • Size: 39.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.12.3

File hashes

Hashes for outpostkit-0.0.69.tar.gz
Algorithm Hash digest
SHA256 d809c406208eb3574957b4c8647d9f445cf7da8aadb4d96d908a7e902295483d
MD5 383b41ee0b74d053ae74a135400acdf6
BLAKE2b-256 ff8f283cd249c3479394f1e8f08fd35db09676ee0b91f3f4a6d328d15d02e5fb

See more details on using hashes here.

File details

Details for the file outpostkit-0.0.69-py3-none-any.whl.

File metadata

  • Download URL: outpostkit-0.0.69-py3-none-any.whl
  • Upload date:
  • Size: 43.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.12.3

File hashes

Hashes for outpostkit-0.0.69-py3-none-any.whl
Algorithm Hash digest
SHA256 59834e21960db799fd4245809eaa7747972c835a875599d4779ef8a9294c1306
MD5 87589b8efad6bb88411f692c13a4d2bb
BLAKE2b-256 5487de13805f5e49cb6efec23258d0fd0fdb6ceee0f5b8f04e0021a82e27df6a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page