yAlerter(BaseAlerter):
    """My alerter class."""def post(
        self, message: str, config: Optional[BaseAlerterStepParameters]
    ) -> bool:
        """Post a message to a chat service."""
        ...
        return "Hey, I implemented an alerter."

def ask(
        self, question: str, config: Optional[BaseAlerterStepParameters]
    ) -> bool:
        """Post a message to a chat service and wait for approval."""
        ...
        return True

If you need to configure your custom alerter, you can also implement a config object.Copyfrom zenml.alerter.base_alerter import BaseAlerterConfig

class MyAlerterConfig(BaseAlerterConfig):
    my_param: str

Finally, you can bring the implementation and the configuration together in a new flavor object.Copyfrom typing import Type, TYPE_CHECKING

from zenml.alerter import BaseAlerterFlavor

if TYPE_CHECKING:
    from zenml.stack import StackComponent, StackComponentConfig

class MyAlerterFlavor(BaseAlerterFlavor):
    @property
    def name(self) -> str:
        return "my_alerter"

@property
    def config_class(self) -> Type[StackComponentConfig]:
        from my_alerter_config import MyAlerterConfig

return MyAlerterConfig

@property
    def implementation_class(self) -> Type[StackComponent]:
        from my_alerter import MyAlerter

return MyAlerter

Once you are done with the implementation, you can register your new flavor through the CLI. Please ensure you point to the flavor class via dot notation:

zenml alerter flavor register <path.to.MyAlerterFlavor>

For example, if your flavor class MyAlerterFlavor is defined in flavors/my_flavor.py, you'd register it by doing:

zenml alerter flavor register flavors.my_flavor.MyAlerterFlavor

ZenML resolves the flavor class by taking the path where you initialized zenml (via zenml init) as the starting point of resolution. Therefore, please ensure you follow the best practice of initializing zenml at the root of your repository.