Skip to content

Conversation

@bfirsh
Copy link
Member

@bfirsh bfirsh commented Feb 8, 2022

This is an implementation of #193 and #205 to use Pydantic as the type annotations for model predictors and FastAPI for serving the model via HTTP.

Models now look a bit like this:

from cog import BasePredictor, Input, Path
import torch

class ColorizationPredictor(BasePredictor):
    def setup(self):
        self.model = torch.load("./weights.pth")

    def predict(self,
          image: Path = Input(title="Grayscale input image")
    ) -> Path:
        processed_input = preprocess(image)
        output = self.model(processed_input)
        return postprocess(output)

This is a follow-up of #378, where the initial working version was made.

At some point, we want to lock down and define the HTTP API, but we are deferring that to a future piece of work. The Redis queue worker remains unchanged and supports the old API for Replicate to run these models.

The branch has now been moved to this repository so we can collaborate together by opening pull requests against it. We aren't putting this work in main because the documentation for the current version of Cog is there.

Upgrading from Cog 0.0 to 0.1

[Note: This documentation should be copied to the release notes when we merge this.]

In Cog versions up to 0.0.20 you described inputs using annotations on your predict method. For example:

@cog.input("image", type=Path, help="Image to enlarge")
@cog.input("scale", type=float, default=1.5, help="Factor to scale image by")
def predict(self, image, scale):
    ...

From Cog 0.1.0 onwards, we've started using Pydantic to define input and output types. Rather than describing inputs using annotations, you now describe them with type hinting. Here's how you'd define the same inputs now:

def predict(self,
    image: Path = Input(description="Image to enlarge"),
    scale: float = Input(description="Factor to scale image by", default=1.5)
) -> Path:
    ...

The parameters that Input() takes are pretty similar to those @cog.input() used to take. Here are the differences:

  • It no longer takes a type parameter; use a type hint instead.
  • The help parameter has been renamed to description.
  • The options parameter has been renamed to choice.
  • The min option has been replaced by two options:
    • ge: greater than or equal to (direct replacement)
    • gt: greater than (a new alternative)
  • The max option has been replaced by two options:
    • le: less than or equal to (direct replacement)
    • lt: less than (a new alternative)

The other major difference is that you now need to define the output type of your model. That's the -> Path bit in the example above. That might be a simple type like str, float or bool. If you need to handle multiple outputs, check out the new documentation for complex output objects. If you only have a single output, but it can be of different types depending on the run, you can use typing.Any:

from typing import Any

def predict(self) -> Any:
    ...

Todo

Future

(These should be migrated to issues.) Done!

Refs

Closes #58
Closes #113
Closes #193
Closes #205
Closes #212
Closes #246
Closes #304
Closes #306
Closes #328

bfirsh and others added 15 commits March 1, 2022 15:54
Signed-off-by: Ben Firshman <[email protected]>
Signed-off-by: Zeke Sikelianos <[email protected]>
..even when the user-defined predict function returns a simple type like a string.

Co-Authored-By: Ben Firshman <[email protected]>
Signed-off-by: Zeke Sikelianos <[email protected]>
Signed-off-by: Zeke Sikelianos <[email protected]>
As a convenience for `from pydantic import BaseModel`

Signed-off-by: Ben Firshman <[email protected]>
Signed-off-by: Zeke Sikelianos <[email protected]>
These all don't seem relevant (and in some cases undocumented?).

I have also removed support for `kwargs`, which just shows up as extra
fields in the schema. What a lovely foot gun that is. They aren't even
put behind `additionalProperties`, or something like that.

Adding support for things is much easier to take it away, so it seems to
be conservative here.

Signed-off-by: Ben Firshman <[email protected]>
evilstreak and others added 10 commits March 1, 2022 15:54
Installing 1.21.1 failed locally because the binary is only available
for older versions of Python.

Although the latest version of numpy is 1.22.2, the latest version that
includes support for Python 3.7 is 1.21.5.

Signed-off-by: Dominic Baggott <[email protected]>
The new Cog internals return a 422 instead of a 400 when the input is
invalid. Rather than dumping that out as a cryptic "returned a 422"
message, pull out the individual validation issues from the response and
present them to the user.

Adding the hint about how to provide inputs on the command line will
hopefully help first time users who are running a bare `cog predict`.

Signed-off-by: Dominic Baggott <[email protected]>
If an output type isn't specified, Pydantic expects the model to return
nothing. When the model returns an output and it's compared to `None`,
an error is logged and a 500 error is returned.

This change explicitly raises an error when the output type is undefined
and raises an error message, rather than defaulting to `None`. The error
message provides explanation of how to fix the issue more clearly than
the original behaviour.

Signed-off-by: Dominic Baggott <[email protected]>
The source of this information is the annotation, but the thing we're
actually using this for is the type of the input. Name it `InputType` to
make that clearer, and for consistency with the variable `OutputType`
used for type checking outputs.

Co-authored-by: Preethi Vaidyanathan <[email protected]>
Signed-off-by: Dominic Baggott <[email protected]>
When presenting the list of allowed types to the user:

- show builtins as just the class name (eg `str`)
- prefix other classes with the module (eg `cog.Path`)

Within Cog, `Path` and `File` are available in the module `cog.types`,
but when Cog is imported into another project they're in the module
`cog`. Special case Cog types to only show the `cog` module.

Added a test to assert an error is raised when starting the server.
Moved, renamed and tweaked the test for extraneous keys, because the
server won't start any more with that predictor definition.

Co-authored-by: Preethi Vaidyanathan <[email protected]>
Signed-off-by: Dominic Baggott <[email protected]>
The message coming from Pydantic isn't quite what we'd write ourselves,
but it's good enough for a model author to debug the issue.

Signed-off-by: Dominic Baggott <[email protected]>
* Removed gt and lt cog.Input options because of a pydantic bug that makes them unusable

Signed-off-by: Dashiell Stander <[email protected]>

* Removed references to gt and lt from documentation

Signed-off-by: Dashiell Stander <[email protected]>
* document cog.Path()

Signed-off-by: Zeke Sikelianos <[email protected]>

* document cog.File()

Signed-off-by: Zeke Sikelianos <[email protected]>

* fix import

Signed-off-by: Zeke Sikelianos <[email protected]>

* whoops don't forget Input

Signed-off-by: Zeke Sikelianos <[email protected]>

* update docs for cog.File and cog.Path

based on feedback from Benzo

Signed-off-by: Zeke Sikelianos <[email protected]>
@bfirsh bfirsh marked this pull request as ready for review March 1, 2022 23:54
Copy link
Member

@zeke zeke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏🏼 PY 👏🏼 DAN 👏🏼 TIC!

:shipit:

@bfirsh
Copy link
Member Author

bfirsh commented Mar 1, 2022

All rebased and merge-conflict-resolved.

@zeke zeke merged commit 703db05 into main Mar 2, 2022
@zeke zeke deleted the future branch March 2, 2022 00:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment