From 422c62f0c5cc4378e9e360e906a8151ab88bac62 Mon Sep 17 00:00:00 2001 From: Luke Russell Date: Thu, 19 Mar 2026 10:03:12 -0700 Subject: [PATCH 1/3] just docs --- docs/english/_sidebar.json | 6 +---- docs/english/experiments.md | 46 ++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/docs/english/_sidebar.json b/docs/english/_sidebar.json index eab9d94f8..61b574617 100644 --- a/docs/english/_sidebar.json +++ b/docs/english/_sidebar.json @@ -85,11 +85,7 @@ "tools/bolt-python/concepts/token-rotation" ] }, - { - "type": "category", - "label": "Experiments", - "items": ["tools/bolt-python/experiments"] - }, + "tools/bolt-python/experiments", { "type": "category", "label": "Legacy", diff --git a/docs/english/experiments.md b/docs/english/experiments.md index 681c8cbc6..e1acf8393 100644 --- a/docs/english/experiments.md +++ b/docs/english/experiments.md @@ -8,6 +8,7 @@ We love feedback from our community, so we encourage you to explore and interact ## Available experiments * [Agent listener argument](#agent) +* [`say_stream` utility](#say-stream) ## Agent listener argument {#agent} @@ -31,4 +32,47 @@ def handle_mention(agent: BoltAgent): ### Limitations -The `chat_stream()` method currently only works when the `thread_ts` field is available in the event context (DMs and threaded replies). Top-level channel messages do not have a `thread_ts` field, and the `ts` field is not yet provided to `BoltAgent`. \ No newline at end of file +The `chat_stream()` method currently only works when the `thread_ts` field is available in the event context (DMs and threaded replies). Top-level channel messages do not have a `thread_ts` field, and the `ts` field is not yet provided to `BoltAgent`. + +## `say_stream` utility {#say-stream} + +The `say_stream` utility is a listener argument available on `app.event` and `app.message` listeners. + +The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. + +| Parameter | Value | +|---|---| +| `channel_id` | Sourced from the event payload. +| `thread_ts` | Sourced from the event payload. Falls back to the `ts` value if available. +| `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). +| `recipient_user_id` | Sourced from the `user_id` of the event. + +If neither a `channel_id` or `thread_ts` can be sourced, then the utility will merely be `None`. + +### Example {#example} + +```py +import os + +from slack_bolt import App, SayStream +from slack_bolt.adapter.socket_mode import SocketModeHandler +from slack_sdk import WebClient + +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) + +@app.event("app_mention") +def handle_app_mention(client: WebClient, say_stream: SayStream): + stream = say_stream() + stream.append(markdown_text="Someone rang the bat signal!") + stream.stop() + +@app.message("") +def handle_message(client: WebClient, say_stream: SayStream): + stream = say_stream() + + stream.append(markdown_text="Let me consult my *vast knowledge database*...) + stream.stop() + +if __name__ == "__main__": + SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start() +``` \ No newline at end of file From 14a4273961e203b557ad60ae4c43579166f4b85e Mon Sep 17 00:00:00 2001 From: Luke Russell Date: Tue, 24 Mar 2026 16:12:29 -0700 Subject: [PATCH 2/3] go --- docs/english/concepts/message-sending.md | 75 ++++++++++++++++-------- docs/english/experiments.md | 43 -------------- 2 files changed, 50 insertions(+), 68 deletions(-) diff --git a/docs/english/concepts/message-sending.md b/docs/english/concepts/message-sending.md index 87c433129..22418db00 100644 --- a/docs/english/concepts/message-sending.md +++ b/docs/english/concepts/message-sending.md @@ -43,37 +43,64 @@ def show_datepicker(event, say): ## Streaming messages {#streaming-messages} -You can have your app's messages stream in to replicate conventional AI chatbot behavior. This is done through three Web API methods: +You can have your app's messages stream in to replicate conventional agent behavior. This is done through three Web API methods: * [`chat_startStream`](/reference/methods/chat.startStream) * [`chat_appendStream`](/reference/methods/chat.appendStream) * [`chat_stopStream`](/reference/methods/chat.stopStream) -The Python Slack SDK provides a [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility to streamline calling these methods. Here's an excerpt from our [Assistant template app](https://github.com/slack-samples/bolt-python-assistant-template): +Bolt for Python provides a `say_stream` listener argument available on `app.event` and `app.message` listeners. -```python -streamer = client.chat_stream( - channel=channel_id, - recipient_team_id=team_id, - recipient_user_id=user_id, - thread_ts=thread_ts, -) - -# Loop over OpenAI response stream -# https://platform.openai.com/docs/api-reference/responses/create -for event in returned_message: - if event.type == "response.output_text.delta": - streamer.append(markdown_text=f"{event.delta}") - else: - continue - -feedback_block = create_feedback_block() -streamer.stop(blocks=feedback_block) +The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. + +| Parameter | Value | +|---|---| +| `channel_id` | Sourced from the event payload. +| `thread_ts` | Sourced from the event payload. Falls back to the `ts` value if available. +| `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). +| `recipient_user_id` | Sourced from the `user_id` of the event. + +If neither a `channel_id` or `thread_ts` can be sourced, then the utility will merely be `None`. + +For information on calling the `chat_*Stream` API methods directly, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. + +### Example {#example} + +```py +import os + +from slack_bolt import App, SayStream +from slack_bolt.adapter.socket_mode import SocketModeHandler +from slack_sdk import WebClient + +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) + +@app.event("app_mention") +def handle_app_mention(client: WebClient, say_stream: SayStream): + stream = say_stream() + stream.append(markdown_text="Someone rang the bat signal!") + stream.stop() + +@app.message("") +def handle_message(client: WebClient, say_stream: SayStream): + stream = say_stream() + + stream.append(markdown_text="Let me consult my *vast knowledge database*...) + stream.stop() + +if __name__ == "__main__": + SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start() ``` -In that example, a [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element is passed to `streamer.stop` to provide feedback buttons to the user at the bottom of the message. Interaction with these buttons will send a block action event to your app to receive the feedback. +#### Adding feedback buttons after a stream -```python +You can pass a [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element to `stream.stop` to provide feedback buttons to the user at the bottom of the message. Interaction with these buttons will send a block action event to your app to receive the feedback. + +```py +stream.stop(blocks=feedback_block) +``` + +```py def create_feedback_block() -> List[Block]: blocks: List[Block] = [ ContextActionsBlock( @@ -95,6 +122,4 @@ def create_feedback_block() -> List[Block]: ) ] return blocks -``` - -For information on calling the `chat_*Stream` API methods without the helper utility, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/english/experiments.md b/docs/english/experiments.md index e1acf8393..7eac4453f 100644 --- a/docs/english/experiments.md +++ b/docs/english/experiments.md @@ -33,46 +33,3 @@ def handle_mention(agent: BoltAgent): ### Limitations The `chat_stream()` method currently only works when the `thread_ts` field is available in the event context (DMs and threaded replies). Top-level channel messages do not have a `thread_ts` field, and the `ts` field is not yet provided to `BoltAgent`. - -## `say_stream` utility {#say-stream} - -The `say_stream` utility is a listener argument available on `app.event` and `app.message` listeners. - -The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. - -| Parameter | Value | -|---|---| -| `channel_id` | Sourced from the event payload. -| `thread_ts` | Sourced from the event payload. Falls back to the `ts` value if available. -| `recipient_team_id` | Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). -| `recipient_user_id` | Sourced from the `user_id` of the event. - -If neither a `channel_id` or `thread_ts` can be sourced, then the utility will merely be `None`. - -### Example {#example} - -```py -import os - -from slack_bolt import App, SayStream -from slack_bolt.adapter.socket_mode import SocketModeHandler -from slack_sdk import WebClient - -app = App(token=os.environ.get("SLACK_BOT_TOKEN")) - -@app.event("app_mention") -def handle_app_mention(client: WebClient, say_stream: SayStream): - stream = say_stream() - stream.append(markdown_text="Someone rang the bat signal!") - stream.stop() - -@app.message("") -def handle_message(client: WebClient, say_stream: SayStream): - stream = say_stream() - - stream.append(markdown_text="Let me consult my *vast knowledge database*...) - stream.stop() - -if __name__ == "__main__": - SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start() -``` \ No newline at end of file From fd123aa01c38c182515e09301c972590e09c95ee Mon Sep 17 00:00:00 2001 From: Luke Russell Date: Tue, 24 Mar 2026 16:12:50 -0700 Subject: [PATCH 3/3] remove experiment --- docs/english/experiments.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/english/experiments.md b/docs/english/experiments.md index 7eac4453f..a89b698ad 100644 --- a/docs/english/experiments.md +++ b/docs/english/experiments.md @@ -8,7 +8,6 @@ We love feedback from our community, so we encourage you to explore and interact ## Available experiments * [Agent listener argument](#agent) -* [`say_stream` utility](#say-stream) ## Agent listener argument {#agent}