pythongoogle-cloud-platformfastapigoogle-cloud-vertex-aiuvicorn

TypeError: __call__() missing 1 required positional argument: 'context' error when deploying on GCP


I want to deploy a FastAPI app in Google Cloud Platform (GCP), specifically with Vertex AI.

These are my files:

main.py

import os

import uvicorn
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from mangum import Mangum

from app.api.api_v0.api import router as api_v0_router

load_dotenv()

root_path = os.getenv('ENV', default="")
app = FastAPI(root_path=f"/{root_path}")

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(api_v0_router, prefix="/api/v0")

handler = Mangum(app)

if __name__ == "__main__":
    uvicorn.run(app, port=8000)

api_v0_router

from fastapi import APIRouter

from app.api.api_v0.endpoints.process import router as process_router
from app.api.api_v0.endpoints.test import router as test_router

router = APIRouter()

router.include_router(process_router)
router.include_router(test_router)

process_router.py

import json
from typing import List

import numpy as np
from fastapi import APIRouter, UploadFile

from app.utils.utils import convert_file_to_image
from onnxruntime import InferenceSession

MODEL_PATH = "./model/last.onnx"

router = APIRouter(prefix="/process", tags=["Process"])

@router.on_event("startup")
def load_inference_session():
    global session
    session = InferenceSession(MODEL_PATH)

@router.post("")
async def root(
        files: List[UploadFile]
):
    file_read = await files[0].read()
    np_image = convert_file_to_image(file_read)
    np_image = np_image.astype(np.float32)
    input_name = session.get_inputs()[0].name
    output_name = session.get_outputs()[0].name

    result = session.run([output_name], {input_name: np_image})

    return json.dumps({"result": str(np.array(result).shape)})

Dockerfile

FROM python:3.9

COPY requirements.txt .

RUN pip install --no-cache-dir Bottleneck==1.3.5
RUN pip install --no-cache-dir -r requirements.txt

COPY ./app /app

EXPOSE 8080

CMD ["uvicorn", "app.main:handler", "--host", "0.0.0.0", "--port", "8080"]

Then I'm deploying my project using the following command:

gcloud builds submit . --tag=<YOUR_REGION>-docker.pkg.dev/<YOUR_PROJECT>/<YOUR_REPOSITORY>/<PROJECT> --machine-type='n1-highcpu-32' --timeout=900s --verbosity=info

On GCP I am:

  1. Creating a Model Registry in Vertex AI
  2. Deploying the model

After some time, these are the logs:

INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 408, in run_asgi
result = await app( # type: ignore[func-returns-value]
File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
return await self.app(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/asgi2.py", line 16, in __call__
instance = self.app(scope)
TypeError: __call__() missing 1 required positional argument: 'context'

According to the logs the error is related to Uvicorn. I've taken a look at different tutorials using their approach, but this error is never mentioned.

I don't have experience at all with GCP so I'm not sure if I'm doing something wrong.


Solution

  • Mangum handler is designed for AWS Lambda, but you are using GCP, so you don't need it and could use a standard FastAPI application:

    import os
    
    import uvicorn.protocols.http.h11_impl
    from dotenv import load_dotenv
    from fastapi import FastAPI
    from fastapi.middleware.cors import CORSMiddleware
    
    from app.api.api_v0.api import router as api_v0_router
    
    load_dotenv()
    
    root_path = os.getenv('ENV', default="")
    app = FastAPI(root_path=f"/{root_path}")
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=['*'],
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    app.include_router(api_v0_router, prefix="/api/v0")
    
    
    if __name__ == "__main__":
        uvicorn.run(app, host="0.0.0.0", port=8080)
    

    After this, you should change the command inside of a Dockerfile:

    CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]
    

    P.S. Dont forget to rebuild and push your docker image after this:

    docker build -t gcr.io/PROJECT_ID/YOUR_IMAGE_NAME .
    docker push gcr.io/PROJECT_ID/YOUR_IMAGE_NAME