Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pip install -r requirements.txt
python3 app.py
```

Start talking to the bot! Start a new DM or thread and click the feedback button when it responds.

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.

@lukegalbraithrussell me trying to check items off of @mwbrooks wishlist 🆘


#### Linting
```zsh
# Run flake8 from root directory for linting
Expand All @@ -74,8 +76,8 @@ black .

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.

:::info[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.py`.
> [!NOTE]

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.

this is good! i have no idea why i suggested to use our docs syntax in a readme - i got mixed up with which repo i was commenting on probably

> 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`.

**`/listeners/assistant`**

Expand Down
2 changes: 2 additions & 0 deletions listeners/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from listeners.assistant import assistant
from listeners.assistant.assistant import handle_feedback
Comment thread
srtaalej marked this conversation as resolved.
Outdated


def register_listeners(app):
# Using assistant middleware is the recommended way.
app.assistant(assistant)
app.action("feedback")(handle_feedback)

# The following event listeners demonstrate how to implement the same on your own.
# from listeners import events
Expand Down
72 changes: 68 additions & 4 deletions listeners/assistant/assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,46 @@
from slack_bolt import Assistant, BoltContext, Say, SetSuggestedPrompts
from slack_bolt.context.get_thread_context import GetThreadContext
from slack_sdk import WebClient
from slack_sdk.models.blocks import FeedbackButtonsElement, FeedbackButtonObject, ContextActionsBlock
Comment thread
srtaalej marked this conversation as resolved.
Outdated

from ..llm_caller import call_llm

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


def create_feedback_block(user_id: str) -> ContextActionsBlock:
Comment thread
srtaalej marked this conversation as resolved.
Outdated
"""
Create feedback block with thumbs up/down buttons

Args:
user_id: User ID for user-specific controls
Comment thread
srtaalej marked this conversation as resolved.
Outdated

Returns:
Block Kit context_actions block
"""
block = [
Comment thread
srtaalej marked this conversation as resolved.
Outdated
ContextActionsBlock(
elements=[
FeedbackButtonsElement(
action_id="feedback",
positive_button=FeedbackButtonObject(
text="Good Response",
accessibility_label="Submit positive feedback on this response",
value="good-feedback",
),
negative_button=FeedbackButtonObject(
text="Bad Response",
accessibility_label="Submit negative feedback on this response",
value="bad-feedback",
),
)
]
)
]
return block
Comment thread
srtaalej marked this conversation as resolved.
Outdated


# This listener is invoked when a human user opened an assistant thread
@assistant.thread_started
def start_assistant_thread(
Expand Down Expand Up @@ -60,6 +93,7 @@ def respond_in_assistant_thread(
say: Say,
):
try:
user_id = payload["user"]
channel_id = payload["channel"]
thread_ts = payload["thread_ts"]

Expand All @@ -83,26 +117,56 @@ def respond_in_assistant_thread(
messages_in_thread.append({"role": role, "content": message["text"]})

returned_message = call_llm(messages_in_thread)

client.assistant_threads_setStatus(
channel_id=channel_id, thread_ts=thread_ts, status="Bolt is typing", loading_messages=loading_messages
)

stream_response = client.chat_startStream(
channel=channel_id,
thread_ts=thread_ts,
)
stream_ts = stream_response["ts"]

# use of this for loop is specific to openai response method
for event in returned_message:
if event.type == "response.output_text.delta":
client.chat_appendStream(channel=channel_id, ts=stream_ts, markdown_text=f"{event.delta}")
else:
continue

client.chat_stopStream(
channel=channel_id,
ts=stream_ts,
)
Comment thread
srtaalej marked this conversation as resolved.
feedback_block = create_feedback_block(user_id=user_id)
client.chat_stopStream(channel=channel_id, ts=stream_ts, blocks=feedback_block)

except Exception as e:
logger.exception(f"Failed to handle a user message event: {e}")
say(f":warning: Something went wrong! ({e})")


# Handle feedback buttons (thumbs up/down)
def handle_feedback(ack, body, client, logger):
ack()
try:
Comment thread
srtaalej marked this conversation as resolved.
Outdated
message_ts = body["message"]["ts"]
channel_id = body["channel"]["id"]
feedback_type = body["actions"][0]["value"]
is_positive = feedback_type == "good-feedback"

if is_positive:
client.chat_postEphemeral(
channel=channel_id,
user=body["user"]["id"],
thread_ts=message_ts,
text="We're glad you found this useful.",
)
Comment thread
srtaalej marked this conversation as resolved.
Outdated
else:
client.chat_postEphemeral(
channel=channel_id,
user=body["user"]["id"],
thread_ts=message_ts,
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.",
)

logger.debug(f"Handled feedback: type={feedback_type}, message_ts={message_ts}")
except Exception as error:
logger.error(f":warning: Something went wrong! {error}")