Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 1 addition & 5 deletions docs/english/_sidebar.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,7 @@
"tools/bolt-python/concepts/token-rotation"
]
},
{
"type": "category",
"label": "Experiments",
"items": ["tools/bolt-python/experiments"]
},
"tools/bolt-python/experiments",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was my bad; should've been a page from the beginning

{
"type": "category",
"label": "Legacy",
Expand Down
75 changes: 50 additions & 25 deletions docs/english/concepts/message-sending.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit(non-blocking): Showing the 3 Web API methods to manually stream messages at the very top is a little confusing. Also, L54 mentions that say_stream uses this API under-the-hood and there's a nice callout at the bottom explaining how to manually use the Web API Streaming methods.

Could this sentence be merged with the next paragraph? For example:

You can have your app's messages stream in to replicate conventional agent behaviour. Bolt for Python provides a say_stream utility is available on app.event and app.message listeners.


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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nice ⭐

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The current sentence is correct, but "argument" is often thought of as a simple variable or object. We use the term "utility" below, so maybe can clarify that say_stream is a utility provided as a listener argument.

Suggested change
Bolt for Python provides a `say_stream` listener argument available on `app.event` and `app.message` listeners.
Bolt for Python provides a `say_stream` utility as a listener argument for `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`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i feel like this can be clearer

Suggested change
If neither a `channel_id` or `thread_ts` can be sourced, then the utility will merely be `None`.
If neither a `channel_id` or `thread_ts` can be sourced, then the utility will just 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(
Expand All @@ -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.
```
2 changes: 1 addition & 1 deletion docs/english/experiments.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ 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`.
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`.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I believe we've fixed this issue so say_stream() is now available on top-level channel messages and uses the ts as the thread_ts value. We've also updated chat_stream() to be say_stream().

.cc @WilliamBergamin

Loading