aboutsummaryrefslogtreecommitdiff
path: root/autogpts/autogpt/autogpt/command_decorator.py
blob: 6902939a0668d1acabb4aebc63ad7a0038d60b4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import re
from typing import Callable, Optional, ParamSpec, TypeVar

from autogpt.core.utils.json_schema import JSONSchema
from autogpt.models.command import Command, CommandOutput, CommandParameter

# Unique identifier for AutoGPT commands
AUTO_GPT_COMMAND_IDENTIFIER = "auto_gpt_command"

P = ParamSpec("P")
CO = TypeVar("CO", bound=CommandOutput)


def command(
    names: list[str] = [],
    description: Optional[str] = None,
    parameters: dict[str, JSONSchema] = {},
) -> Callable[[Callable[P, CommandOutput]], Command]:
    """
    The command decorator is used to make a Command from a function.

    Args:
        names (list[str]): The names of the command.
            If not provided, the function name will be used.
        description (str): A brief description of what the command does.
            If not provided, the docstring until double line break will be used
            (or entire docstring if no double line break is found)
        parameters (dict[str, JSONSchema]): The parameters of the function
            that the command executes.
    """

    def decorator(func: Callable[P, CO]) -> Command:
        doc = func.__doc__ or ""
        # If names is not provided, use the function name
        command_names = names or [func.__name__]
        # If description is not provided, use the first part of the docstring
        if not (command_description := description):
            if not func.__doc__:
                raise ValueError("Description is required if function has no docstring")
            # Return the part of the docstring before double line break or everything
            command_description = re.sub(r"\s+", " ", doc.split("\n\n")[0].strip())

        # Parameters
        typed_parameters = [
            CommandParameter(
                name=param_name,
                spec=spec,
            )
            for param_name, spec in parameters.items()
        ]

        # Wrap func with Command
        command = Command(
            names=command_names,
            description=command_description,
            method=func,
            parameters=typed_parameters,
        )

        return command

    return decorator