-
Notifications
You must be signed in to change notification settings - Fork 399
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
docs: concept focused nav #2302
Changes from 5 commits
6b3a430
5cbb4f8
ac518e1
b99edba
9c53077
9268140
4041ac6
0f2e6fd
2118ff1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the shortcuts and commands pages, we combine both listening and responding into one doc. Maybe we could do the same for actions? I.e. combine |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ You’ll notice in all `action()` examples, `ack()` is used. It is required to c | |
|
||
:::info | ||
|
||
Since v2, message shortcuts (previously message actions) now use the `shortcut()` method instead of the `action()` method. View the [migration guide for V2](/tutorial/migration-v2) to learn about the changes. | ||
Since v2, message shortcuts (previously message actions) now use the `shortcut()` method instead of the `action()` method. View the [migration guide for V2](/migration/migration-v2) to learn about the changes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's just get rid of this callout; we're on v4 now. No need to link or call out things specific about a version that was last released 4 years ago. |
||
|
||
::: | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
--- | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Grabbing this from other PR so I can display Assistant in Nav There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we can merge this change ealier than the assistant page PR, removing this from this PR, merging this PR without assistant page, and then adjusting the assistant page PR to be compatible with this new structure may be smoother |
||
title: Agents & Assistants | ||
lang: en | ||
slug: /concepts/assistant | ||
--- | ||
|
||
:::info[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. | ||
::: | ||
|
||
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! | ||
|
||
## Configuring your app to support assistants | ||
|
||
1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & Assistants** feature. | ||
|
||
2. Within the App Settings **OAuth & Permissions** page, add the following scopes: | ||
* [`assistant:write`](https://api.slack.com/scopes/assistant:write) | ||
* [`chat:write`](https://api.slack.com/scopes/chat:write) | ||
* [`im:history`](https://api.slack.com/scopes/im:history) | ||
|
||
3 Within the App Settings **Event Subscriptions** page, subscribe to the following events: | ||
* [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started) | ||
* [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed) | ||
* [`message.im`](https://api.slack.com/events/message.im) | ||
|
||
:::info | ||
You _could_ implement your own assistants by [listening](/concepts/event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events. That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! | ||
::: | ||
|
||
## The `Assistant` class | ||
|
||
### Instance template | ||
|
||
```ts | ||
const assistant = new Assistant({ | ||
threadContextStore: { | ||
get: async ({ context, client, payload }) => {}, | ||
save: async ({ context, client, payload }) => {}, | ||
}, | ||
threadStarted: async ({ say, saveThreadContext, setStatus, setSuggestedPrompts, setTitle }) => {}, | ||
threadContextChanged: async ({ say, setStatus, setSuggestedPrompts, setTitle }) => {}, | ||
userMessage: async ({ say, getThreadContext, setStatus, setSuggestedPrompts, setTitle }) => {}, | ||
}); | ||
``` | ||
|
||
### The `AssistantConfig` configuration object | ||
|
||
| Property | Required? | Description | | ||
|---|---|---| | ||
|`threadContextStore` | Optional, but recommended | When provided, must have the required methods to get and save thread context, which will override the `getThreadContext` and `saveThreadContext` utilities. <br/> <br/> If not provided, a `DefaultAssistantContextStore` instance is used. | ||
| `threadStarted` | Required | Executes when the user opens the assistant container or otherwise begins a new chat, thus sending the [`assistant_thread_started`](https://api.slack.com/events/assistant_thread_started) event. | ||
| `threadContextChanged` | Optional | Executes when a user switches channels while the assistant container is open, thus sending the [`assistant_thread_context_changed`](https://api.slack.com/events/assistant_thread_context_changed) event. <br/> <br/> If not provided, context will be saved using the AssistantContextStore's `save` method (either the `DefaultAssistantContextStore` instance or provided `threadContextStore`). | ||
| `userMessage` | Required | Executes when a [message](https://api.slack.com/events/message), thus sending the [`message.im`](https://api.slack.com/events/message.im) event. These messages do not contain a subtype and must be deduced based on their shape and metadata (if provided). Bolt handles this deduction out of the box for those using the `Assistant` class. | ||
|
||
### Utilities | ||
|
||
Utility | Description | ||
|---|---| | ||
| `getThreadContext` | Alias for `AssistantContextStore.get()` method. Executed if custom `AssistantContextStore` value is provided. <br/><br/> If not provided, the `DefaultAssistantContextStore` instance will retrieve the most recent context saved to the instance. | ||
| `saveThreadContext` | Alias for `AssistantContextStore.save()`. Executed if `AssistantContextStore` value is provided. <br/> <br/> If not provided, the `DefaultAssistantContextStore` instance will save the `assistant_thread.context` to the instance and attach it to the initial assistant message that was sent to the thread. | ||
| `say(message: string)` | Alias for the `postMessage` method.<br/><br/> Sends a message to the current assistant thread. | ||
| `setTitle(title: string)` | [Sets the title](https://api.slack.com/methods/assistant.threads.setTitle) of the assistant thread to capture the initial topic/question. | ||
| `setStatus(status: string)` | Sets the [status](https://api.slack.com/methods/assistant.threads.setStatus) of the assistant to give the appearance of active processing. | ||
| `setSuggestedPrompts({ prompts: [{ title: string; message: string; }]` | Provides the user up to 4 optional, preset [prompts](https://api.slack.com/methods/assistant.threads.setSuggestedPrompts) to choose from. | ||
|
||
## Handling a new thread | ||
|
||
The `threadStarted` event handler allows your app to respond to new threads opened by users. In the example below, the app is sending a message — containing context message metadata — to the user, along with a single [prompt](https://api.slack.com/methods/assistant.threads.setSuggestedPrompts). | ||
|
||
```js | ||
const assistant = new Assistant({ | ||
threadStarted: async ({ event, say, setSuggestedPrompts, saveThreadContext }) => { | ||
const { context } = event.assistant_thread; | ||
|
||
await say({ | ||
text: 'Hi, how can I help?', | ||
metadata: { event_type: 'assistant_thread_context', event_payload: context }, | ||
}); | ||
|
||
await saveThreadContext(); | ||
|
||
const prompts = [{ | ||
title: 'Fun Slack fact', | ||
message: 'Give me a fun fact about Slack, please!', | ||
}]; | ||
|
||
// Provide the user up to 4 optional, preset prompts to choose from. | ||
await setSuggestedPrompts({ prompts }); | ||
}, | ||
... | ||
``` | ||
|
||
:::tip | ||
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 using the `getThreadContext()` utility, as subsequent user message event payloads won't include the channel info. | ||
::: | ||
|
||
|
||
## Handling context changes | ||
|
||
You can store context through the `threadContextStore` property but it must feature `get` and `save` methods. | ||
|
||
```js | ||
threadContextStore: { | ||
get: async ({ context, client, payload }) => {}, | ||
save: async ({ context, client, payload }) => {}, | ||
}, | ||
``` | ||
|
||
If not provided, a `DefaultThreadContextStore` instance is utilized instead, which is a reference implementation that relies on storing and retrieving message metadata as the context changes. | ||
|
||
When the user switches channels, the `assistant_thread_context_changed` event will be sent to your app. Capture this with the `threadContextChanged` handler. | ||
|
||
```js | ||
... | ||
threadContextChanged: async ({ saveThreadContext }) => { | ||
await saveThreadContext(); | ||
}, | ||
... | ||
``` | ||
|
||
If you use the built-in `AssistantThreadContextStore` without any custom configuration the updated context data is automatically saved as message metadata on the first reply from the assistant bot. | ||
|
||
## Handling the user response | ||
|
||
User messages are handled with the `userMessage` event handler. The `setTitle` and `setStatus` utilities are useful in curating the user experience. | ||
|
||
:::warning | ||
Messages sent to the assistant do not contain a subtype and must be deduced based on their shape and any provided metadata. | ||
::: | ||
|
||
```js | ||
... | ||
userMessage: async ({ client, message, say, setTitle, setStatus }) => { | ||
const { channel, thread_ts } = message; | ||
|
||
// Set the title of the Assistant thread to capture the initial topic/question | ||
await setTitle(message.text); | ||
|
||
// Set the status of the Assistant to give the appearance of active processing. | ||
await setStatus('is typing..'); | ||
|
||
// Retrieve the Assistant thread history for context of question being asked | ||
const thread = await client.conversations.replies({ | ||
channel, | ||
ts: thread_ts, | ||
oldest: thread_ts, | ||
}); | ||
|
||
// Prepare and tag each message for LLM processing | ||
const userMessage = { role: 'user', content: message.text }; | ||
const threadHistory = thread.messages.map((m) => { | ||
const role = m.bot_id ? 'assistant' : 'user'; | ||
return { role, content: m.text }; | ||
}); | ||
|
||
const messages = [ | ||
{ role: 'system', content: DEFAULT_SYSTEM_CONTENT }, | ||
...threadHistory, | ||
userMessage, | ||
]; | ||
|
||
// Send message history and newest question to LLM | ||
const llmResponse = await openai.chat.completions.create({ | ||
model: 'gpt-4o-mini', | ||
n: 1, | ||
messages, | ||
}); | ||
|
||
// Provide a response to the user | ||
await say(llmResponse.choices[0].message.content); | ||
}, | ||
}); | ||
|
||
app.assistant(assistant); | ||
``` | ||
|
||
## Full example | ||
|
||
<details> | ||
<summary>App Agent & Assistant Template</summary> | ||
|
||
```js reference title="app.js" | ||
https://github.com/slack-samples/bolt-js-assistant-template/blob/main/app.js | ||
``` | ||
</details> |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some end-user or api.slack.com document we can link to for Slash Commands? |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ lang: en | |
slug: /concepts/event-listening | ||
--- | ||
|
||
You can listen to [any Events API event](https://api.slack.com/events) using the `event()` method after subscribing to it in your app configuration. This allows your app to take action when something happens in Slack, like a user reacting to a message or joining a channel. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this isn't exactly part of your PR here but figured I'd mention it: the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense to me! |
||
You can listen to any [Events API event](https://api.slack.com/events) using the `event()` method after subscribing to it in your app configuration. This allows your app to take action when something happens in Slack, like a user reacting to a message or joining a channel. | ||
|
||
The `event()` method requires an `eventType` of type string. | ||
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to change the title of this document so it's a bit more obvious what it's about. "Options" on its own lacks context. It's about dynamic rendering of dropdown select menu options - I'd like to make that more clear in this document as well as in the sidebar. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think one thing we should call out in this document is that, while these interactivity and shortcut-related app features do require acknowledgement of Slack events, the Events API handlers do not because Bolt automatically acknowledges them. Perhaps this particular convenience/magic needs reinforcement on this page and possibly also on the 'listening to messages' and 'listening to events' pages?