|
| 1 | +import logging |
| 2 | +from typing import List, Dict |
| 3 | +from slack_bolt import Assistant, BoltContext, Say, SetSuggestedPrompts, SetStatus |
| 4 | +from slack_bolt.context.get_thread_context import GetThreadContext |
| 5 | +from slack_sdk import WebClient |
| 6 | +from slack_sdk.errors import SlackApiError |
| 7 | + |
| 8 | +from .llm_caller import call_llm |
| 9 | + |
| 10 | +# Refer to https://tools.slack.dev/bolt-python/concepts/assistant/ for more details |
| 11 | +assistant = Assistant() |
| 12 | + |
| 13 | + |
| 14 | +# This listener is invoked when a human user opened an assistant thread |
| 15 | +@assistant.thread_started |
| 16 | +def start_assistant_thread( |
| 17 | + say: Say, |
| 18 | + get_thread_context: GetThreadContext, |
| 19 | + set_suggested_prompts: SetSuggestedPrompts, |
| 20 | + logger: logging.Logger, |
| 21 | +): |
| 22 | + try: |
| 23 | + say("How can I help you?") |
| 24 | + |
| 25 | + prompts: List[Dict[str, str]] = [ |
| 26 | + { |
| 27 | + "title": "What does Slack stand for?", |
| 28 | + "message": "Slack, a business communication service, was named after an acronym. Can you guess what it stands for?", |
| 29 | + }, |
| 30 | + { |
| 31 | + "title": "Write a draft announcement", |
| 32 | + "message": "Can you write a draft announcement about a new feature my team just released? It must include how impactful it is.", |
| 33 | + }, |
| 34 | + { |
| 35 | + "title": "Suggest names for my Slack app", |
| 36 | + "message": "Can you suggest a few names for my Slack app? The app helps my teammates better organize information and plan priorities and action items.", |
| 37 | + }, |
| 38 | + ] |
| 39 | + |
| 40 | + thread_context = get_thread_context() |
| 41 | + if thread_context is not None and thread_context.channel_id is not None: |
| 42 | + summarize_channel = { |
| 43 | + "title": "Summarize the referred channel", |
| 44 | + "message": "Can you generate a brief summary of the referred channel?", |
| 45 | + } |
| 46 | + prompts.append(summarize_channel) |
| 47 | + |
| 48 | + set_suggested_prompts(prompts=prompts) |
| 49 | + except Exception as e: |
| 50 | + logger.exception(f"Failed to handle an assistant_thread_started event: {e}", e) |
| 51 | + say(f":warning: Something went wrong! ({e})") |
| 52 | + |
| 53 | + |
| 54 | +# This listener is invoked when the human user sends a reply in the assistant thread |
| 55 | +@assistant.user_message |
| 56 | +def respond_in_assistant_thread( |
| 57 | + payload: dict, |
| 58 | + logger: logging.Logger, |
| 59 | + context: BoltContext, |
| 60 | + set_status: SetStatus, |
| 61 | + get_thread_context: GetThreadContext, |
| 62 | + client: WebClient, |
| 63 | + say: Say, |
| 64 | +): |
| 65 | + try: |
| 66 | + user_message = payload["text"] |
| 67 | + set_status("is typing...") |
| 68 | + |
| 69 | + if user_message == "Can you generate a brief summary of the referred channel?": |
| 70 | + # the logic here requires the additional bot scopes: |
| 71 | + # channels:join, channels:history, groups:history |
| 72 | + thread_context = get_thread_context() |
| 73 | + referred_channel_id = thread_context.get("channel_id") |
| 74 | + try: |
| 75 | + channel_history = client.conversations_history(channel=referred_channel_id, limit=50) |
| 76 | + except SlackApiError as e: |
| 77 | + if e.response["error"] == "not_in_channel": |
| 78 | + # If this app's bot user is not in the public channel, |
| 79 | + # we'll try joining the channel and then calling the same API again |
| 80 | + client.conversations_join(channel=referred_channel_id) |
| 81 | + channel_history = client.conversations_history(channel=referred_channel_id, limit=50) |
| 82 | + else: |
| 83 | + raise e |
| 84 | + |
| 85 | + prompt = f"Can you generate a brief summary of these messages in a Slack channel <#{referred_channel_id}>?\n\n" |
| 86 | + for message in reversed(channel_history.get("messages")): |
| 87 | + if message.get("user") is not None: |
| 88 | + prompt += f"\n<@{message['user']}> says: {message['text']}\n" |
| 89 | + messages_in_thread = [{"role": "user", "content": prompt}] |
| 90 | + returned_message = call_llm(messages_in_thread) |
| 91 | + say(returned_message) |
| 92 | + return |
| 93 | + |
| 94 | + replies = client.conversations_replies( |
| 95 | + channel=context.channel_id, |
| 96 | + ts=context.thread_ts, |
| 97 | + oldest=context.thread_ts, |
| 98 | + limit=10, |
| 99 | + ) |
| 100 | + messages_in_thread: List[Dict[str, str]] = [] |
| 101 | + for message in replies["messages"]: |
| 102 | + role = "user" if message.get("bot_id") is None else "assistant" |
| 103 | + messages_in_thread.append({"role": role, "content": message["text"]}) |
| 104 | + returned_message = call_llm(messages_in_thread) |
| 105 | + say(returned_message) |
| 106 | + |
| 107 | + except Exception as e: |
| 108 | + logger.exception(f"Failed to handle a user message event: {e}") |
| 109 | + say(f":warning: Something went wrong! ({e})") |
0 commit comments