Skip to content

Commit d9ee954

Browse files
Docs: Update Bolt Java nav and Assistant doc to match JS structure. (#1434)
Co-authored-by: Kazuhiro Sera <[email protected]>
1 parent d29a29a commit d9ee954

File tree

9 files changed

+502
-419
lines changed

9 files changed

+502
-419
lines changed

docs/content/guides/assistants.md

Lines changed: 153 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,37 @@
11
---
2+
title: Agents & Assistants
23
lang: en
34
---
45

5-
# Agents & Assistants
6+
:::info This feature requires a paid plan
67

7-
This guide focuses on how to implement Agents & Assistants using Bolt. For general information about the feature, please refer to the [API documentation](https://api.slack.com/docs/apps/ai).
8+
If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free.
89

9-
## Slack App Configuration
10+
:::
1011

11-
To get started, you'll need to enable the **Agents & Assistants** feature on [the app configuration page](https://api.slack.com/apps). Then, add [`assistant:write`](https://api.slack.com/scopes/assistant:write), [`chat:write`](https://api.slack.com/scopes/chat:write), and [`im:history`](https://api.slack.com/scopes/im:history) to the **bot** scopes on the **OAuth & Permissions** page. Also, make sure to subscribe to [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started), [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed), and [`message.im`](https://api.slack.com/events/message.im) events on the **Event Subscriptions** page.
12+
Agents and assistants comprise a new messaging experience for Slack. If you're unfamiliar with using agents and assistants within Slack, you'll want to read the [API documentation on the subject](https://api.slack.com/docs/apps/ai). Then come back here to implement them with Bolt!
1213

13-
Please note that this feature requires a paid plan. If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free.
14+
## Configuring your app to support assistants {#configuring-your-app}
1415

15-
## Examples
16+
1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & Assistants** feature.
1617

17-
To handle assistant thread interactions with humans, although you can implement your agents using `app.event(...)` listeners for `assistant_thread_started`, `assistant_thread_context_changed`, and `message` events, Bolt offers a simpler approach. You just need to create an `Assistant` instance, attach the needed event handlers to it, and then add the assistant to your `App` instance.
18+
2. Within the App Settings **OAuth & Permissions** page, add the following scopes:
19+
* [`assistant:write`](https://api.slack.com/scopes/assistant:write)
20+
* [`chat:write`](https://api.slack.com/scopes/chat:write)
21+
* [`im:history`](https://api.slack.com/scopes/im:history)
22+
23+
3. Within the App Settings **Event Subscriptions** page, subscribe to the following events:
24+
* [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started)
25+
* [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed)
26+
* [`message.im`](https://api.slack.com/events/message.im)
27+
28+
## The `Assistant` class instance {#assistant-class}
29+
30+
The [`Assistant`](/reference#the-assistantconfig-configuration-object) class can be used to handle the incoming events expected from a user interacting with an assistant in Slack. A typical flow would look like:
31+
32+
1. [The user starts a thread](#handling-a-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started) event.
33+
2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed) events. The class also provides a default `context` store to keep track of thread context changes as the user moves through Slack.
34+
3. [The user responds](#handling-user-response). The `Assistant` class handles the incoming [`message.im`](https://api.slack.com/events/message.im) event.
1835

1936
```java
2037
App app = new App();
@@ -54,29 +71,29 @@ assistant.userMessage((req, ctx) -> {
5471
app.assistant(assistant);
5572
```
5673

57-
When a user opens an Assistant thread while in a channel, the channel information is stored as the thread's `AssistantThreadContext` data. You can access this information by using the `get_thread_context` utility. The reason Bolt provides this utility is that the most recent thread context information is not included in the subsequent user message event payload data. Therefore, an app must store the context data when it is changed so that the app can refer to the data in message event listeners.
74+
While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context the `threadContextStore` property — but it also provides a `DefaultThreadContextStore` instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](https://api.slack.com/metadata/using) as the user interacts with the assistant.
5875

59-
When the user switches channels, the `assistant_thread_context_changed` event will be sent to your app. If you use the built-in `Assistant` middleware without any custom configuration (like the above code snippet does), the updated context data is automatically saved as message metadata of the first reply from the assistant bot.
76+
If you do provide your own `threadContextStore` property, it must feature `get` and `save` methods.
6077

61-
As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history` which are used to look up the stored message metadata that contains the thread context (via `context.getThreadContextService().findCurrentContext(channelId, threadTs)`).
78+
:::tip
79+
Be sure to give the [assistants reference docs](/reference#agents--assistants) a look!
80+
:::
6281

63-
If you prefer storing this data elsewhere, you can pass your own `AssistantThreadContextService` implementation to the `Assistant` instance:
82+
## Handling a new thread {#handling-a-new-thread}
6483

65-
```java
66-
Assistant assistant = new Assistant(new YourOwnAssistantThreadContextService());
67-
```
84+
When the user opens a new thread with your assistant, the [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started) event will be sent to your app.
6885

69-
<details>
86+
:::tip
87+
When a user opens an assistant thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data. You can grab that info by using the `context.getThreadContext()` utility, as subsequent user message event payloads won't include the channel info.
88+
:::
7089

71-
<summary>
72-
Block Kit interactions in the assistant thread
73-
</summary>
90+
### Block Kit interactions in the assistant thread {#block-kit-interactions}
7491

7592
For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](https://api.slack.com/metadata) to trigger subsequent interactions with the user.
7693

7794
For example, an app can display a button like "Summarize the referring channel" in the initial reply. When the user clicks the button and submits detailed information (such as the number of messages, days to check, the purpose of the summary, etc.), the app can handle that information and post a message that describes the request with structured metadata.
7895

79-
By default, your app can't respond to its own bot messages (Bolt prevents infinite loops by default). However, if you set `ignoringSelfAssistantMessageEventsEnabled` to false and add a `botMessage` listener to your `Assistant` middleware, your app can continue processing the request as shown below:
96+
By default, apps can't respond to their own bot messages (Bolt prevents infinite loops by default). However, if you set `ignoringSelfAssistantMessageEventsEnabled` to false and add a `botMessage` listener to your `Assistant` middleware, your app can continue processing the request as shown below:
8097

8198
```java
8299
App app = new App(AppConfig.builder()
@@ -156,4 +173,120 @@ assistant.userMessage((req, ctx) -> {
156173
app.assistant(assistant);
157174
```
158175

159-
</details>
176+
## Handling thread context changes {#handling-thread-context-changes}
177+
178+
When the user switches channels, the [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed) event will be sent to your app.
179+
180+
If you use the built-in `Assistant` middleware without any custom configuration, the updated context data is automatically saved as [message metadata](https://api.slack.com/metadata/using) of the first reply from the assistant bot.
181+
182+
As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history`, which are used to look up the stored message metadata that contains the thread context (via `context.getThreadContextService().findCurrentContext(channelId, threadTs)`).
183+
184+
If you prefer storing this data elsewhere, you can pass your own custom `AssistantThreadContextService` implementation to the `Assistant` constructor. We provide `DefaultAssistantThreadContextService`, which is a reference implementation that uses the assistant thread message metadata. You can use this for production apps, but if you want to use a different datastore for it, you can implement your own class that inherits `AssistantThreadContextService` interface.
185+
186+
```java
187+
Assistant assistant = new Assistant(new YourOwnAssistantThreadContextService());
188+
```
189+
190+
## Handling the user response {#handling-user-response}
191+
192+
When the user messages your assistant, the [`message.im`](https://api.slack.com/events/message.im) event will be sent to your app.
193+
194+
Messages sent to the assistant do not contain a [subtype](https://api.slack.com/events/message#subtypes) and must be deduced based on their shape and any provided [message metadata](https://api.slack.com/metadata/using).
195+
196+
There are three utilities that are particularly useful in curating the user experience:
197+
* [`say`](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/#slack_bolt.Say)
198+
* [`setTitle`](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/#slack_bolt.SetTitle)
199+
* [`setStatus`](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/#slack_bolt.SetStatus)
200+
201+
## Full example: Assistant Simple App {#full-example}
202+
203+
Below is the `AssistantSimpleApp.java` file of the [Assistant Template repo](https://github.com/slackapi/java-slack-sdk/tree/d29a29afff9f2a518495d618502cb7b292e2eb14/bolt-socket-mode/src/test/java/samples) we've created for you to build off of.
204+
205+
```java
206+
package samples;
207+
208+
import com.slack.api.bolt.App;
209+
import com.slack.api.bolt.AppConfig;
210+
import com.slack.api.bolt.middleware.builtin.Assistant;
211+
import com.slack.api.bolt.socket_mode.SocketModeApp;
212+
import com.slack.api.model.assistant.SuggestedPrompt;
213+
import com.slack.api.model.event.AppMentionEvent;
214+
import com.slack.api.model.event.MessageEvent;
215+
216+
import java.util.Collections;
217+
218+
public class AssistantSimpleApp {
219+
220+
public static void main(String[] args) throws Exception {
221+
String botToken = System.getenv("SLACK_BOT_TOKEN");
222+
String appToken = System.getenv("SLACK_APP_TOKEN");
223+
224+
App app = new App(AppConfig.builder().singleTeamBotToken(botToken).build());
225+
226+
Assistant assistant = new Assistant(app.executorService());
227+
228+
assistant.threadStarted((req, ctx) -> {
229+
try {
230+
ctx.say("Hi, how can I help you today?");
231+
ctx.setSuggestedPrompts(r -> r
232+
.title("Select one of the following:") // optional
233+
.prompts(Collections.singletonList(SuggestedPrompt.create("What does SLACK stand for?")))
234+
);
235+
} catch (Exception e) {
236+
ctx.logger.error("Failed to handle assistant thread started event: {e}", e);
237+
}
238+
});
239+
240+
assistant.userMessage((req, ctx) -> {
241+
try {
242+
// ctx.setStatus(r -> r.status("is typing...")); works too
243+
ctx.setStatus("is typing...");
244+
Thread.sleep(500L);
245+
if (ctx.getThreadContext() != null) {
246+
String contextChannel = ctx.getThreadContext().getChannelId();
247+
ctx.say("I am ware of the channel context: <#" + contextChannel + ">");
248+
} else {
249+
ctx.say("Here you are!");
250+
}
251+
} catch (Exception e) {
252+
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
253+
try {
254+
ctx.say(":warning: Sorry, something went wrong during processing your request!");
255+
} catch (Exception ee) {
256+
ctx.logger.error("Failed to inform the error to the end-user: {ee}", ee);
257+
}
258+
}
259+
});
260+
261+
assistant.userMessageWithFiles((req, ctx) -> {
262+
try {
263+
ctx.setStatus("is analyzing the files...");
264+
Thread.sleep(500L);
265+
ctx.setStatus("is still checking the files...");
266+
Thread.sleep(500L);
267+
ctx.say("Your files do not have any issues!");
268+
} catch (Exception e) {
269+
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
270+
try {
271+
ctx.say(":warning: Sorry, something went wrong during processing your request!");
272+
} catch (Exception ee) {
273+
ctx.logger.error("Failed to inform the error to the end-user: {ee}", ee);
274+
}
275+
}
276+
});
277+
278+
app.use(assistant);
279+
280+
app.event(MessageEvent.class, (req, ctx) -> {
281+
return ctx.ack();
282+
});
283+
284+
app.event(AppMentionEvent.class, (req, ctx) -> {
285+
ctx.say("You can help you at our 1:1 DM!");
286+
return ctx.ack();
287+
});
288+
289+
new SocketModeApp(appToken, app).start();
290+
}
291+
}
292+
```

docs/content/guides/events-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
lang: en
33
---
44

5-
# Events API
5+
# Events
66

77
The [Events API](https://api.slack.com/events-api) is a streamlined way to build apps and bots that respond to activities in Slack. All you need is a Slack app and a secure place for us to send your events.
88

0 commit comments

Comments
 (0)