Skip to content

Commit fd1e840

Browse files
committed
chore: merge w base branch
2 parents a749a2d + c4ba883 commit fd1e840

7 files changed

Lines changed: 92 additions & 17 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ pip install -r requirements.txt
5151
python3 app.py
5252
```
5353

54+
Start talking to the bot! Start a new DM or thread and click the feedback button when it responds.
55+
5456
#### Linting
5557
```zsh
5658
# Run flake8 from root directory for linting
@@ -74,8 +76,8 @@ black .
7476

7577
Every incoming request is routed to a "listener". This directory groups each listener based on the Slack Platform feature used, so `/listeners/events` handles incoming events, `/listeners/shortcuts` would handle incoming [Shortcuts](https://docs.slack.dev/interactivity/implementing-shortcuts/) requests, and so on.
7678

77-
:::info[The `listeners/events` folder is purely educational and demonstrates alternative approaches to implementation]
78-
These listeners are **not registered** and are not used in the actual application. For the working implementation, refer to `listeners/assistant.py`.
79+
> [!NOTE]
80+
> The `listeners/events` folder is purely educational and demonstrates alternative approaches to implementation. These listeners are **not registered** and are not used in the actual application. For the working implementation, refer to `listeners/assistant/assistant.py`.
7981
8082
**`/listeners/assistant`**
8183

listeners/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
from listeners.assistant import assistant
1+
from listeners import actions
2+
from listeners import assistant
23

34

45
def register_listeners(app):
5-
# Using assistant middleware is the recommended way.
6-
app.assistant(assistant)
6+
7+
actions.register(app)
8+
assistant.register(app)
79

810
# The following event listeners demonstrate how to implement the same on your own.
911
# from listeners import events

listeners/actions/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from slack_bolt import App
2+
from .actions import handle_feedback
3+
4+
5+
def register(app: App):
6+
app.action("feedback")(handle_feedback)

listeners/actions/actions.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import logging
2+
3+
4+
# Handle feedback buttons (thumbs up/down)
5+
def handle_feedback(ack, body, client, logger: logging.Logger):
6+
try:
7+
ack()
8+
message_ts = body["message"]["ts"]
9+
channel_id = body["channel"]["id"]
10+
feedback_type = body["actions"][0]["value"]
11+
is_positive = feedback_type == "good-feedback"
12+
13+
if is_positive:
14+
client.chat_postEphemeral(
15+
channel=channel_id,
16+
user=body["user"]["id"],
17+
thread_ts=message_ts,
18+
text="We're glad you found this useful.",
19+
)
20+
else:
21+
client.chat_postEphemeral(
22+
channel=channel_id,
23+
user=body["user"]["id"],
24+
thread_ts=message_ts,
25+
text="Sorry to hear that response wasn't up to par :slightly_frowning_face: Starting a new chat may help with AI mistakes and hallucinations.",
26+
)
27+
28+
logger.debug(f"Handled feedback: type={feedback_type}, message_ts={message_ts}")
29+
except Exception as error:
30+
logger.error(f":warning: Something went wrong! {error}")

listeners/assistant/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from slack_bolt import App
12
from .assistant import assistant
23

3-
__all__ = ["assistant"]
4+
5+
def register(app: App):
6+
app.assistant(assistant)

listeners/assistant/assistant.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,49 @@
11
import logging
22
from typing import Dict, List
33

4-
from slack_bolt import Assistant, BoltContext, Say, SetStatus, SetSuggestedPrompts
4+
from slack_bolt import (Assistant, BoltContext, Say, SetStatus,
5+
SetSuggestedPrompts)
56
from slack_bolt.context.get_thread_context import GetThreadContext
67
from slack_sdk import WebClient
8+
from slack_sdk.models.blocks import (Block, ContextActionsBlock,
9+
FeedbackButtonObject,
10+
FeedbackButtonsElement)
711

812
from ..llm_caller import call_llm
913

1014
# Refer to https://tools.slack.dev/bolt-python/concepts/assistant/ for more details
1115
assistant = Assistant()
1216

1317

18+
def create_feedback_block() -> List[Block]:
19+
"""
20+
Create feedback block with thumbs up/down buttons
21+
22+
Returns:
23+
Block Kit context_actions block
24+
"""
25+
blocks: List[Block] = [
26+
ContextActionsBlock(
27+
elements=[
28+
FeedbackButtonsElement(
29+
action_id="feedback",
30+
positive_button=FeedbackButtonObject(
31+
text="Good Response",
32+
accessibility_label="Submit positive feedback on this response",
33+
value="good-feedback",
34+
),
35+
negative_button=FeedbackButtonObject(
36+
text="Bad Response",
37+
accessibility_label="Submit negative feedback on this response",
38+
value="bad-feedback",
39+
),
40+
)
41+
]
42+
)
43+
]
44+
return blocks
45+
46+
1447
# This listener is invoked when a human user opened an assistant thread
1548
@assistant.thread_started
1649
def start_assistant_thread(
@@ -73,6 +106,11 @@ def respond_in_assistant_thread(
73106
"Convincing the AI to stop overthinking…",
74107
]
75108

109+
set_status(
110+
status="Drafting...",
111+
loading_messages=loading_messages,
112+
)
113+
76114
replies = client.conversations_replies(
77115
channel=context.channel_id,
78116
ts=context.thread_ts,
@@ -85,26 +123,21 @@ def respond_in_assistant_thread(
85123
messages_in_thread.append({"role": role, "content": message["text"]})
86124

87125
returned_message = call_llm(messages_in_thread)
88-
set_status(
89-
status="Drafting...",
90-
loading_messages=loading_messages,
91-
)
92126
stream_response = client.chat_startStream(
93127
channel=channel_id,
94128
thread_ts=thread_ts,
95129
)
96130
stream_ts = stream_response["ts"]
131+
97132
# use of this for loop is specific to openai response method
98133
for event in returned_message:
99134
if event.type == "response.output_text.delta":
100135
client.chat_appendStream(channel=channel_id, ts=stream_ts, markdown_text=f"{event.delta}")
101136
else:
102137
continue
103138

104-
client.chat_stopStream(
105-
channel=channel_id,
106-
ts=stream_ts,
107-
)
139+
feedback_block = create_feedback_block()
140+
client.chat_stopStream(channel=channel_id, ts=stream_ts, blocks=feedback_block)
108141

109142
except Exception as e:
110143
logger.exception(f"Failed to handle a user message event: {e}")

requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
--extra-index-url=https://test.pypi.org/simple/
2-
slack_sdk==3.36.0.dev2
1+
slack-sdk==3.36.0.dev3
32
slack-bolt>=1.21,<2
43

54
# If you use a different LLM vendor, replace this dependency

0 commit comments

Comments
 (0)