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

Updated Slack Alerter #3282

Merged
merged 10 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
Binary file modified docs/book/.gitbook/assets/slack-alerter-oauth-permissions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/book/.gitbook/assets/slack-alerter-token.jpg
Binary file not shown.
Binary file added docs/book/.gitbook/assets/slack-alerter-token.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
250 changes: 168 additions & 82 deletions docs/book/component-guide/alerters/slack.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,143 +4,229 @@ description: Sending automated alerts to a Slack channel.

# Slack Alerter

The `SlackAlerter` enables you to send messages to a dedicated Slack channel directly from within your ZenML pipelines.
The `SlackAlerter` enables you to send messages or ask questions within a
dedicated Slack channel directly from within your ZenML pipelines and steps.

The `slack` integration contains the following two standard steps:
## How to Create

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

Interacting with Slack from within your pipelines can be very useful in practice:
In order to use the `SlackAlerter`, you first need to have a Slack workspace
set up with a channel that you want your pipelines to post to.

* The `slack_alerter_post_step` allows you to get notified immediately when failures happen (e.g., model performance degradation, data drift, ...),
* The `slack_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 `SlackAlerter`, you first need to install ZenML's `slack` integration:

```shell
zenml integration install slack -y
```

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

### Setting Up a Slack Bot

In order to use the `SlackAlerter`, you first need to have a Slack workspace set up with a channel that you want your pipelines to post to.

Then, you need to [create a Slack App](https://api.slack.com/apps?new\_app=1) with a bot in your workspace.

{% hint style="info" %}
Make sure to give your Slack bot the following permissions in the `OAuth & Permissions` tab under `Scopes`:
Then, you need to [create a Slack App](https://api.slack.com/apps?new\_app=1)
with a bot in your workspace. Make sure to give it the following permissions in
the `OAuth & Permissions` tab under `Scopes`:

* `chat:write`,
* `chat:write.public`
* `channels:read`
* `groups:read`
* `im:read`
* `mpim:read`
* `channels:history`

![Slack OAuth Permissions](../../.gitbook/assets/slack-alerter-oauth-permissions.png)

In order to be able to use the `ask()` functionality, you need to invite the app
to your channel. You can either use the `/invite` command directly in the
desired channel or add it through the channel settings:

![Slack OAuth Permissions](../../.gitbook/assets/slack-channel-settings.png)

{% hint style="warning" %}
It might take some time for your app to register within your workspace and
show up in the available list of applications.
{% endhint %}

### Registering a Slack Alerter in ZenML

Next, you need to register a `slack` alerter in ZenML and link it to the bot you just created. You can do this with the following commands:
To create a `SlackAlerter`, you first need to install ZenML's `slack`
integration:

```shell
zenml integration install slack -y
```

Once the integration is installed, you can use the ZenML CLI to create a
secret and register an alerter linked to the app you just created:

```shell
zenml secret create slack_token --oauth_token=<SLACK_TOKEN>
zenml alerter register slack_alerter \
--flavor=slack \
--slack_token='{{ slack_token:oauth_token }}' \
--default_slack_channel_id=<SLACK_CHANNEL_ID>
--slack_token={{slack_token.oauth_token}} \
--slack_channel_id=<SLACK_CHANNEL_ID>
```

Here is where you can find the required parameters:

* `<SLACK_CHANNEL_ID>`: Open your desired Slack channel in a browser, and copy out the last part of the URL starting with `C....`.
* `<SLACK_TOKEN>`: This is the Slack token of your bot. You can find it in the Slack app settings under `OAuth & Permissions`. **IMPORTANT**: Please make sure that the token is the `Bot User OAuth Token` not the `User OAuth Token`.
* `<SLACK_CHANNEL_ID>`: The channel ID can be found in the channel details.
It starts with `C....`.
* `<SLACK_TOKEN>`: This is the Slack token of your bot. You can find it in the
Slack app settings under `OAuth & Permissions`.

![Slack Token Image](../../.gitbook/assets/slack-alerter-token.jpg)
![Slack Token Image](../../.gitbook/assets/slack-alerter-token.png)

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

```shell
zenml stack register ... -al slack_alerter
zenml stack register ... -al slack_alerter --set
```

### How to Use the Slack Alerter
## How to Use

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

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.
### Use the `post()` and `ask()` directly

As an example, adding `slack_alerter_ask_step()` to your pipeline could look like this:
You can use the client to fetch the active alerter within your stack and
use the `post` and `ask` methods directly:

```python
from zenml.integrations.slack.steps.slack_alerter_ask_step import slack_alerter_ask_step
from zenml import step, pipeline
from zenml import pipeline, step
from zenml.client import Client


@step
def post_statement() -> None:
Client().active_stack.alerter.post("Step finished!")

@step
def my_formatter_step(artifact_to_be_communicated) -> str:
return f"Here is my artifact {artifact_to_be_communicated}!"
def ask_question() -> bool:
return Client().active_stack.alerter.ask("Should I continue?")


@pipeline(enable_cache=False)
def my_pipeline():
# Step using alerter.post
post_statement()

# Step using alerter.ask
ask_question()

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

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

An example of adding a custom Slack block as part of any alerter logic for your pipeline could look like this:

### Use it with custom settings

The Slack alerter comes equipped with a set of options that you can set during
runtime:

```python
from typing import List, Dict
from zenml.integrations.slack.steps.slack_alerter_ask_step import slack_alerter_post_step
from zenml.integrations.slack.alerters.slack_alerter import SlackAlerterParameters
from zenml import step, pipeline
from zenml import pipeline, step
from zenml.client import Client


# E.g, You can use a different channel ID through the settings. However, if you
# want to use the `ask` functionality, make sure that you app is invited to
# this channel first.
@step(settings={"alerter": {"slack_channel_id": <SLACK_CHANNEL_ID>}})
def post_statement() -> None:
alerter = Client().active_stack.alerter
alerter.post("Posting to another channel!")


@pipeline(enable_cache=False)
def my_pipeline():
# Using alerter.post
post_statement()


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

## Use it with `SlackAlerterParameters` and `SlackAlerterPayload`

You can use these additional classes to further edit your messages:

```python
from zenml import pipeline, step, get_step_context
from zenml.client import Client
from zenml.integrations.slack.alerters.slack_alerter import (
SlackAlerterParameters, SlackAlerterPayload
)


# Displaying pipeline info
@step
def my_custom_block_step(block_message) -> List[Dict]:
def post_statement() -> None:
params = SlackAlerterParameters(
payload=SlackAlerterPayload(
pipeline_name=get_step_context().pipeline.name,
step_name=get_step_context().step_run.name,
stack_name=Client().active_stack.name,

),

)
Client().active_stack.alerter.post(
message="This is a message with additional information about your pipeline.",
params=params
)


# Formatting with blocks
@step
def ask_question() -> bool:
message = ":tada: Should I continue? (Y/N)"
my_custom_block = [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f":tada: {block_message}",
"emoji": true
}
}
]
return SlackAlerterParameters(blocks = my_custom_block)


@pipeline
def my_pipeline(...):
...
message_blocks = my_custom_block_step("my custom block!")
post_message = slack_alerter_post_step(params = message_blocks)
return post_message
{
"type": "header",
"text": {
"type": "plain_text",
"text": message,
"emoji": True
}
}
]
params = SlackAlerterParameters(
blocks=my_custom_block,
approve_msg_options=["Y"],
disapprove_msg_options=["N"],

)
return Client().active_stack.alerter.ask(question=message, params=params)


@pipeline(enable_cache=False)
def my_pipeline():
post_statement()
ask_question()


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

### Use the predefined steps

If you want to only use it in a simple manner, you can also use the steps
`slack_alerter_post_step` and `slack_alerter_ask_step`, that are built-in to
the Slack integration of ZenML:

```python
from zenml import pipeline
from zenml.integrations.slack.steps.slack_alerter_post_step import (
slack_alerter_post_step
)
from zenml.integrations.slack.steps.slack_alerter_ask_step import (
slack_alerter_ask_step,
)


@pipeline(enable_cache=False)
def my_pipeline():
slack_alerter_post_step("Posting a statement.")
slack_alerter_ask_step("Asking a question. Should I continue?")


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

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

<figure><img src="https://static.scarf.sh/a.png?x-pxid=f0b4f458-0a54-4fcd-aa95-d5ee424815bc" alt="ZenML Scarf"><figcaption></figcaption></figure>
3 changes: 1 addition & 2 deletions src/zenml/integrations/slack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ class SlackIntegration(Integration):
"""

NAME = SLACK
REQUIREMENTS = ["slack-sdk>=3.16.1", "aiohttp>=3.8.1"]
REQUIREMENTS_IGNORED_ON_UNINSTALL = ["aiohttp"]
REQUIREMENTS = ["slack-sdk==3.30.0"]

@classmethod
def flavors(cls) -> List[Type[Flavor]]:
Expand Down
Loading
Loading