Repository: Significant-Gravitas/Auto-GPT
File: autogpt_platform/backend/backend/util/service.py (Python)

Code before:
        """Access the RabbitMQ config. Will raise if not configured."""
        if not self.rabbitmq_config:
            raise RuntimeError("RabbitMQ not configured for this service")
        return self.rabbitmq_config

    def run_service(self) -> None:
        while True:
            time.sleep(10)

    def run_and_wait(self, coro: Coroutine[Any, Any, T]) -> T:
        return asyncio.run_coroutine_threadsafe(coro, self.shared_event_loop).result()

    def _create_fastapi_endpoint(self, func: Callable) -> Callable:
        """
        Generates a FastAPI endpoint for the given function, handling default and optional parameters.

        :param func: The original function (sync/async, bound or unbound)
        :return: A FastAPI endpoint function.
        """
        sig = inspect.signature(func)
        fields = {}

        is_bounded_method = False
        for name, param in sig.parameters.items():
            if name in ("self", "cls"):
                is_bounded_method = True
                continue

            # Use the provided annotation or fallback to str if not specified
            annotation = (
                param.annotation if param.annotation != inspect.Parameter.empty else str
            )

            # If a default value is provided, use it; otherwise, mark the field as required with '...'
            default = param.default if param.default != inspect.Parameter.empty else ...

            fields[name] = (annotation, default)

        # Dynamically create a Pydantic model for the request body
        RequestBodyModel = create_model("RequestBodyModel", **fields)
        f = func.__get__(self) if is_bounded_method else func

        if asyncio.iscoroutinefunction(f):

            async def async_endpoint(body: RequestBodyModel):  # type: ignore
                try:
                    return await f(
                        **{name: getattr(body, name) for name in body.model_fields}
                    )
                except Exception as e:
                    logger.exception(f"Error in {func.__name__}: {e}")

Review comment (suggestion):
```suggestion
        is_bound_method = False
        for name, param in sig.parameters.items():
            if name in ("self", "cls"):
                is_bound_method = True
```

Code after:
def fastapi_expose(func: C) -> C:
    func = getattr(func, "__func__", func)
    setattr(func, "__exposed__", True)
    return func


def fastapi_exposed_run_and_wait(
    f: Callable[P, Coroutine[None, None, R]]
) -> Callable[Concatenate[object, P], R]:
    # TODO:
    #  This function lies about its return type to make the DynamicClient
    #  call the function synchronously, fix this when DynamicClient can choose
    #  to call a function synchronously or asynchronously.
    return expose(f)  # type: ignore


# ----- Begin Pyro Expose Block ---- #
def pyro_expose(func: C) -> C:
    """
    Decorator to mark a method or class to be exposed for remote calls.

    ## ⚠️ Gotcha
    Aside from "simple" types, only Pydantic models are passed unscathed *if annotated*.
    Any other passed or returned class objects are converted to dictionaries by Pyro.
    """

    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            msg = f"Error in {func.__name__}: {e}"
            if isinstance(e, ValueError):
                logger.warning(msg)
            else:
                logger.exception(msg)
            raise

    register_pydantic_serializers(func)

    return pyro.expose(wrapper)  # type: ignore


def register_pydantic_serializers(func: Callable):
    """Register custom serializers and deserializers for annotated Pydantic models"""
    for name, annotation in func.__annotations__.items():
        try:
            pydantic_types = _pydantic_models_from_type_annotation(annotation)
        except Exception as e:
            raise TypeError(f"Error while exposing {func.__name__}: {e}")

        for model in pydantic_types: