# Creating Components

## The minimal component

Components can be used to implement various functionalities like providing messages to the prompt, executing code, or interacting with external services.

*Component* is a class that inherits from `AgentComponent` OR implements one or more *protocols*. Every *protocol* inherits `AgentComponent`, so your class automatically becomes a *component* once you inherit any *protocol*.

```py
class MyComponent(AgentComponent):
    pass
```

This is already a valid component, but it doesn't do anything yet. To add some functionality to it, you need to implement one or more *protocols*.

Let's create a simple component that adds "Hello World!" message to the agent's prompt. To do this we need to implement `MessageProvider` *protocol* in our component. `MessageProvider` is an interface with `get_messages` method:

```py
# No longer need to inherit AgentComponent, because MessageProvider already does it
class HelloComponent(MessageProvider):
    def get_messages(self) -> Iterator[ChatMessage]:
        yield ChatMessage.user("Hello World!")
```

Now we can add our component to an existing agent or create a new Agent class and add it there:

```py
class MyAgent(Agent):
    self.hello_component = HelloComponent()
```

`get_messages` will called by the agent each time it needs to build a new prompt and the yielded messages will be added accordingly.  

## Passing data to and between components

Since components are regular classes you can pass data (including other components) to them via the `__init__` method.
For example we can pass a config object and then retrieve an API key from it when needed:

```py
class DataComponent(MessageProvider):
    def __init__(self, config: Config):
        self.config = config

    def get_messages(self) -> Iterator[ChatMessage]:
        if self.config.openai_credentials.api_key:
            yield ChatMessage.system("API key found!")
        else:
            yield ChatMessage.system("API key not found!")
```

!!! note
    Component-specific configuration handling isn't implemented yet.

## Configuring components

Components can be configured using a pydantic model.
To make component configurable, it must inherit from `ConfigurableComponent[BM]` where `BM` is the configuration class inheriting from pydantic's `BaseModel`.
You should pass the configuration instance to the `ConfigurableComponent`'s `__init__` or set its `config` property directly.
Using configuration allows you to load confugration from a file, and also serialize and deserialize it easily for any agent.
To learn more about configuration, including storing sensitive information and serialization see [Component Configuration](./components.md#component-configuration).

```py
# Example component configuration
class UserGreeterConfiguration(BaseModel):
    user_name: str

class UserGreeterComponent(MessageProvider, ConfigurableComponent[UserGreeterConfiguration]):
    def __init__(self):
        # Creating configuration instance
        # You could also pass it to the component constructor
        # e.g. `def __init__(self, config: UserGreeterConfiguration):`
        config = UserGreeterConfiguration(user_name="World")
        # Passing the configuration instance to the parent class
        UserGreeterComponent.__init__(self, config)
        # This has the same effect as the line above:
        # self.config = UserGreeterConfiguration(user_name="World")

    def get_messages(self) -> Iterator[ChatMessage]:
        # You can use the configuration like a regular model
        yield ChatMessage.system(f"Hello, {self.config.user_name}!")
```

## Providing commands

To extend what an agent can do, you need to provide commands using `CommandProvider` protocol. For example to allow agent to multiply two numbers, you can create a component like this:

```py
class MultiplicatorComponent(CommandProvider):
    def get_commands(self) -> Iterator[Command]:
        # Yield the command so the agent can use it
        yield self.multiply

    @command(
    parameters={
        "a": JSONSchema(
            type=JSONSchema.Type.INTEGER,
            description="The first number",
            required=True,
        ),
        "b": JSONSchema(
            type=JSONSchema.Type.INTEGER,
            description="The second number",
            required=True,
        )})
    def multiply(self, a: int, b: int) -> str:
        """
        Multiplies two numbers.
        
        Args:
            a: First number
            b: Second number

        Returns:
            Result of multiplication
        """
        return str(a * b)
```

To learn more about commands see [🛠️ Commands](./commands.md).

## Prompt structure

After components provided all necessary data, the agent needs to build the final prompt that will be send to a llm.
Currently, `PromptStrategy` (*not* a protocol) is responsible for building the final prompt.

If you want to change the way the prompt is built, you need to create a new `PromptStrategy` class, and then call relevant methods in your agent class.
You can have a look at the default strategy used by the AutoGPT Agent: [OneShotAgentPromptStrategy](https://github.com/Significant-Gravitas/AutoGPT/tree/master/classic/original_autogpt/agents/prompt_strategies/one_shot.py), and how it's used in the [Agent](https://github.com/Significant-Gravitas/AutoGPT/tree/master/classic/original_autogpt/agents/agent.py) (search for `self.prompt_strategy`).

## Example `UserInteractionComponent`

Let's create a slightly simplified version of the component that is used by the built-in agent.
It gives an ability for the agent to ask user for input in the terminal.

1. Create a class for the component that inherits from `CommandProvider`.

    ```py
    class MyUserInteractionComponent(CommandProvider):
        """Provides commands to interact with the user."""
        pass
    ```

2. Implement command method that will ask user for input and return it.

    ```py
    def ask_user(self, question: str) -> str:
        """If you need more details or information regarding the given goals,
        you can ask the user for input."""
        print(f"\nQ: {question}")
        resp = input("A:")
        return f"The user's answer: '{resp}'"
    ```

3. The command needs to be decorated with `@command`.

    ```py
    @command(
        parameters={
            "question": JSONSchema(
                type=JSONSchema.Type.STRING,
                description="The question or prompt to the user",
                required=True,
            )
        },
    )
    def ask_user(self, question: str) -> str:
        """If you need more details or information regarding the given goals,
        you can ask the user for input."""
        print(f"\nQ: {question}")
        resp = input("A:")
        return f"The user's answer: '{resp}'"
    ```

4. We need to implement `CommandProvider`'s `get_commands` method to yield the command.

    ```py
    def get_commands(self) -> Iterator[Command]:
        yield self.ask_user
    ```

5. Since agent isn't always running in the terminal or interactive mode, we need to disable this component by setting `self._enabled=False` when it's not possible to ask for user input.

    ```py
    def __init__(self, interactive_mode: bool):
        self.config = config
        self._enabled = interactive_mode
    ```

The final component should look like this:

```py
# 1.
class MyUserInteractionComponent(CommandProvider):
    """Provides commands to interact with the user."""

    # We pass config to check if we're in noninteractive mode
    def __init__(self, interactive_mode: bool):
        self.config = config
        # 5.
        self._enabled = interactive_mode

    # 4.
    def get_commands(self) -> Iterator[Command]:
        # Yielding the command so the agent can use it
        # This won't be yielded if the component is disabled
        yield self.ask_user

    # 3.
    @command(
        # We need to provide a schema for ALL the command parameters
        parameters={
            "question": JSONSchema(
                type=JSONSchema.Type.STRING,
                description="The question or prompt to the user",
                required=True,
            )
        },
    )
    # 2.
    # Command name will be its method name and description will be its docstring
    def ask_user(self, question: str) -> str:
        """If you need more details or information regarding the given goals,
        you can ask the user for input."""
        print(f"\nQ: {question}")
        resp = input("A:")
        return f"The user's answer: '{resp}'"
```

Now if we want to use our user interaction *instead of* the default one we need to somehow remove the default one (if our agent inherits from `Agent` the default one is inherited) and add our own. We can simply override the `user_interaction` in `__init__` method:

```py
class MyAgent(Agent):
    def __init__(
        self,
        settings: AgentSettings,
        llm_provider: MultiProvider,
        file_storage: FileStorage,
        app_config: Config,
    ):
        # Call the parent constructor to bring in the default components
        super().__init__(settings, llm_provider, file_storage, app_config)
        # Disable the default user interaction component by overriding it
        self.user_interaction = MyUserInteractionComponent()
```

Alternatively we can disable the default component by setting it to `None`:

```py
class MyAgent(Agent):
    def __init__(
        self,
        settings: AgentSettings,
        llm_provider: MultiProvider,
        file_storage: FileStorage,
        app_config: Config,
    ):
        # Call the parent constructor to bring in the default components
        super().__init__(settings, llm_provider, file_storage, app_config)
        # Disable the default user interaction component
        self.user_interaction = None
        # Add our own component
        self.my_user_interaction = MyUserInteractionComponent(app_config)
```

## Learn more

The best place to see more examples is to look at the built-in components in the [classic/original_autogpt/components](https://github.com/Significant-Gravitas/AutoGPT/tree/master/classic/original_autogpt/components/) and [classic/original_autogpt/commands](https://github.com/Significant-Gravitas/AutoGPT/tree/master/classic/original_autogpt/commands/) directories.

Guide on how to extend the built-in agent and build your own: [🤖 Agents](./agents.md)  
Order of some components matters, see [🧩 Components](./components.md) to learn more about components and how they can be customized.  
To see built-in protocols with accompanying examples visit [⚙️ Protocols](./protocols.md).
