Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discord alerter integration #1818

Merged
merged 19 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ for building human-in-the-loop ML.

## Alerter Flavors

Currently, the [SlackAlerter](slack.md) is the only available alerter integration. However, it is straightforward to
Currently, the [SlackAlerter](slack.md) and [DiscordAlerter](discord.md) are the available alerter integrations. However, it is straightforward to
extend ZenML and [build an alerter for other chat services](custom.md).

| Alerter | Flavor | Integration | Notes |
|------------------------------------|----------|-------------|--------------------------------------------------------------------|
| [Slack](slack.md) | `slack` | `slack` | Interacts with a Slack channel |
| [Custom Implementation](custom.md) | _custom_ | | Extend the alerter abstraction and provide your own implementation |
| Alerter | Flavor | Integration | Notes |
|------------------------------------|-----------|-------------|--------------------------------------------------------------------|
| [Slack](slack.md) | `slack` | `slack` | Interacts with a Slack channel |
| [Discord](discord.md) | `discord` | `discord` | Interacts with a Discord channel |
| [Custom Implementation](custom.md) | _custom_ | | Extend the alerter abstraction and provide your own implementation |

{% hint style="info" %}
If you would like to see the available flavors of alerters in your terminal, you can use the following command:
Expand Down
138 changes: 138 additions & 0 deletions docs/book/stacks-and-components/component-guide/alerters/discord.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
description: Sending automated alerts to a Discord channel.
---

# Discord Alerter

The `DiscordAlerter` enables you to send messages to a dedicated Discord channel
directly from within your ZenML pipelines.

The `discord` integration contains the following two standard steps:

* [discord\_alerter\_post\_step](https://sdkdocs.zenml.io/latest/integration\_code\_docs/integrations-discord/#zenml.integrations.discord.steps.discord\_alerter\_post\_step.discord\_alerter\_post\_step)
takes a string message, posts it to a Discord channel, and returns whether the
operation was successful.
* [discord\_alerter\_ask\_step](https://sdkdocs.zenml.io/latest/integration\_code\_docs/integrations-discord/#zenml.integrations.discord.steps.discord\_alerter\_ask\_step.discord\_alerter\_ask\_step)
also posts a message to a Discord channel, but waits for user feedback, and
only returns `True` if a user explicitly approved the operation from within
Discord (e.g., by sending "approve" / "reject" to the bot in response).

Interacting with Discord from within your pipelines can be very useful in practice:

* The `discord_alerter_post_step` allows you to get notified immediately when failures happen (e.g., model performance
degradation, data drift, ...),
* The `discord_alerter_ask_step` allows you to integrate a human-in-the-loop into your pipelines before executing critical
steps, such as deploying new models.

## How to use it

### Requirements

Before you can use the `DiscordAlerter`, you first need to install ZenML's `discord` integration:

```shell
zenml integration install discord -y
```

{% hint style="info" %}
See the [Integrations](../integration-overview.md) page for more details on ZenML integrations and how to install and
use them.
{% endhint %}

### Setting Up a Discord Bot

In order to use the `DiscordAlerter`, you first need to have a Discord workspace set up with a channel that you want your
pipelines to post to. This is the `<DISCORD_CHANNEL_ID>` you will need when registering the discord alerter component.

Then, you need to [create a Discord App with a bot in your server](https://discordpy.readthedocs.io/en/latest/discord.html)
.

{% hint style="info" %}
Note in the bot token copy step, if you don't find the copy button then click on reset token to reset the bot
and you will get a new token which you can use. Also, make sure you give necessary permissions to the bot
required for sending and receiving messages.
{% endhint %}

### Registering a Discord Alerter in ZenML

Next, you need to register a `discord` alerter in ZenML and link it to the bot you just created. You can do this with the
following command:

```shell
zenml alerter register discord_alerter \
--flavor=discord \
--discord_token=<DISCORD_TOKEN> \
--default_discord_channel_id=<DISCORD_CHANNEL_ID>
```

After you have registered the `discord_alerter`, you can add it to your stack like this:

```shell
zenml stack register ... -al discord_alerter
```

Here is where you can find the required parameters:

#### DISCORD_CHANNEL_ID

Open the discord server, then right-click on the text channel and click on the
'Copy Channel ID' option.

{% hint style="info" %}
If you don't see any 'Copy Channel ID' option for your channel, go to "User Settings" > "Advanced" and make sure "Developer Mode" is active.
{% endhint %}

#### DISCORD_TOKEN

This is the Discord token of your bot. You can find the instructions on how to set up a bot, invite it to your channel, and find its token
[here](https://discordpy.readthedocs.io/en/latest/discord.html).

{% hint style="warning" %}
When inviting the bot to your channel, make sure it has at least the following
permissions:
* Read Messages/View Channels
* Send Messages
* Send Messages in Threads
{% endhint %}

### How to Use the Discord Alerter

After you have a `DiscordAlerter` configured in your stack, you can directly import
the [discord\_alerter\_post\_step](https://sdkdocs.zenml.io/latest/integration\_code\_docs/integrations-discord/#zenml.integrations.discord.steps.discord\_alerter\_post\_step.discord\_alerter\_post\_step)
and [discord\_alerter\_ask\_step](https://sdkdocs.zenml.io/latest/integration\_code\_docs/integrations-discord/#zenml.integrations.discord.steps.discord\_alerter\_ask\_step.discord\_alerter\_ask\_step)
steps and use them in your pipelines.

Since these steps expect a string message as input (which needs to be the output of another step), you typically also
need to define a dedicated formatter step that takes whatever data you want to communicate and generates the string
message that the alerter should post.

As an example, adding `discord_alerter_ask_step()` to your pipeline could look like this:

```python
from zenml.integrations.discord.steps.discord_alerter_ask_step import discord_alerter_ask_step
from zenml import step, pipeline


@step
def my_formatter_step(artifact_to_be_communicated) -> str:
return f"Here is my artifact {artifact_to_be_communicated}!"


@pipeline
def my_pipeline(...):
...
artifact_to_be_communicated = ...
message = my_formatter_step(artifact_to_be_communicated)
approved = discord_alerter_ask_step(message)
... # Potentially have different behavior in subsequent steps if `approved`

if __name__ == "__main__":
my_pipeline()
```

For more information and a full list of configurable attributes of the Discord alerter, check out
the [API Docs](https://sdkdocs.zenml.io/latest/integration\_code\_docs/integrations-discord/#zenml.integrations.discord.alerters.discord\_alerter.DiscordAlerter)
.

<!-- For scarf -->
<figure><img alt="ZenML Scarf" referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=f0b4f458-0a54-4fcd-aa95-d5ee424815bc" /></figure>
1 change: 1 addition & 0 deletions src/zenml/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from zenml.integrations.azure import AzureIntegration # noqa
from zenml.integrations.bentoml import BentoMLIntegration # noqa
from zenml.integrations.deepchecks import DeepchecksIntegration # noqa
from zenml.integrations.discord import DiscordIntegration # noqa
from zenml.integrations.evidently import EvidentlyIntegration # noqa
from zenml.integrations.facets import FacetsIntegration # noqa
from zenml.integrations.feast import FeastIntegration # noqa
Expand Down
1 change: 1 addition & 0 deletions src/zenml/integrations/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
BENTOML = "bentoml"
DASH = "dash"
DEEPCHECKS = "deepchecks"
DISCORD = "discord"
EVIDENTLY = "evidently"
FACETS = "facets"
FEAST = "feast"
Expand Down
47 changes: 47 additions & 0 deletions src/zenml/integrations/discord/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) ZenML GmbH 2022. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing
# permissions and limitations under the License.
"""Discord integration for alerter components."""

from typing import List, Type

from zenml.enums import StackComponentType
from zenml.integrations.constants import DISCORD
from zenml.integrations.integration import Integration
from zenml.stack import Flavor

DISCORD_ALERTER_FLAVOR = "discord"


class DiscordIntegration(Integration):
"""Definition of a Discord integration for ZenML.

Implemented using [Discord API Wrapper](https://pypi.org/project/discord.py/).
"""

NAME = DISCORD
REQUIREMENTS = ["discord.py>=2.3.2", "aiohttp>=3.8.1", "asyncio"]

@classmethod
def flavors(cls) -> List[Type[Flavor]]:
"""Declare the stack component flavors for the Discord integration.

Returns:
List of new flavors defined by the Discord integration.
"""
from zenml.integrations.discord.flavors import DiscordAlerterFlavor

return [DiscordAlerterFlavor]


DiscordIntegration.check_installation()
17 changes: 17 additions & 0 deletions src/zenml/integrations/discord/alerters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing
# permissions and limitations under the License.
"""Alerter components defined by the Discord integration."""
from zenml.integrations.discord.alerters.discord_alerter import ( # noqa
DiscordAlerter,
)

__all__ = ["DiscordAlerter"]
Loading
Loading