    def _create_api_endpoint(
        self,
        api: InferenceAPI[t.Any],
    ) -> t.Callable[[Request], t.Coroutine[t.Any, t.Any, Response]]:
        """
        Create api function for starlette route, it wraps around user defined API
        callback and adapter class, and adds request logging and instrument metrics
        """
        from starlette.concurrency import run_in_threadpool  # type: ignore
        from starlette.responses import JSONResponse

        from ..utils import is_async_callable

        async def api_func(request: Request) -> Response:
            # handle_request may raise 4xx or 5xx exception.
            output = None
            with self.bento_service.context.in_request(request) as ctx:
                try:
                    input_data = await api.input.from_http_request(request)
                    if api.multi_input:
                        if api.needs_ctx:
                            input_data[api.ctx_param] = ctx
                        if is_async_callable(api.func):
                            output = await api.func(**input_data)
                        else:
                            output = await run_in_threadpool(api.func, **input_data)
                    else:
                        args = (input_data,)
                        if api.needs_ctx:
                            args = (input_data, ctx)
                        if is_async_callable(api.func):
                            output = await api.func(*args)
                        else:
                            output = await run_in_threadpool(api.func, *args)

                    response = await api.output.to_http_response(
                        output, ctx if api.needs_ctx else None
                    )

                except BentoMLException as e:
                    log_exception(request, sys.exc_info())
                    if output is not None:
                        import inspect

                        signature = inspect.signature(api.output.to_proto)
                        param = next(iter(signature.parameters.values()))
                        ann = ""
                        if param is not inspect.Parameter.empty:
                            ann = param.annotation

                        # more descriptive errors if output is available
                        logger.error(
                            "Function '%s' has 'input=%s,output=%s' as IO descriptor, and returns 'result=%s', while expected return type is '%s'",
                            api.name,
                            api.input,
                            api.output,
                            type(output),
                            ann,
                        )

                    status = e.error_code.value
                    if 400 <= status < 500 and status not in (401, 403):
                        response = JSONResponse(
                            content="BentoService error handling API request: %s"
                            % str(e),
                            status_code=status,
                        )
                    else:
                        response = JSONResponse("", status_code=status)
                except Exception:  # pylint: disable=broad-except
                    # For all unexpected error, return 500 by default. For example,
                    # if users' model raises an error of division by zero.
                    log_exception(request, sys.exc_info())

                    response = JSONResponse(
                        "An error has occurred in BentoML user code when handling this request, find the error details in server logs",
                        status_code=500,
                    )
                if trace_context.request_id is not None:
                    response.headers["X-BentoML-Request-ID"] = str(
                        trace_context.request_id
                    )
                if (
                    BentoMLContainer.http.response.trace_id.get()
                    and trace_context.trace_id is not None
                ):
                    response.headers["X-BentoML-Trace-ID"] = str(trace_context.trace_id)
                return response

        return api_func
Create api function for starlette route, it wraps around user defined API
callback and adapter class, and adds request logging and instrument metrics
src/bentoml/_internal/server/http_app.py