diff --git a/examples/slackbot/Notebook.ipynb b/examples/slackbot/Notebook.ipynb index 87884222f..0708159eb 100644 --- a/examples/slackbot/Notebook.ipynb +++ b/examples/slackbot/Notebook.ipynb @@ -146,7 +146,7 @@ "\n", "# Format for the OpenAI API\n", "def format_prompt(prompt):\n", - " return \"start -> \" + \"\\n\\n\".join([f' {msg.user} --> {msg.text} ' for msg in prompt]) + \"\\n\\n###\\n\\n\"\n", + " return \"start -> \" + \"\\n\\n\".join([f' {msg[\"user\"]} --> {msg[\"text\"]} ' for msg in prompt]) + \"\\n\\n###\\n\\n\"\n", "examples_df.prompt = examples_df.prompt.apply(format_prompt)\n", "\n", "def format_completion(completion):\n", @@ -216,7 +216,7 @@ "source": [ "import json, math\n", "\n", - "min_prob_for_response = 0.75\n", + "min_prob_for_response = 0.50\n", "\n", "# Receive Slack messages in real-time\n", "live_messages = kt.sources.ArrowSource(entity_column=\"channel\", time_column=\"ts\")\n", @@ -226,8 +226,13 @@ " # Acknowledge the message back to Slack\n", " client.send_socket_mode_response(SocketModeResponse(envelope_id=req.envelope_id))\n", " \n", - " # Deliver the message to Kaskada\n", - " live_messages.add_data(pyarrow.json.read_json(req.payload))\n", + " if req.type == \"events_api\" and \"event\" in req.payload:\n", + " e = req.payload[\"event\"]\n", + " if \"previous_message\" in e or e[\"type\"] == \"reaction_added\":\n", + " return\n", + " # send message events to Kaskada\n", + " live_messages.add_event(pyarrow.json.read_json(e))\n", + "\n", "slack.socket_mode_request_listeners.append(handle_message)\n", "slack.connect()\n", "\n", @@ -239,18 +244,20 @@ " \n", " # Ask the model who should be notified\n", " res = openai.Completion.create(\n", - " model=\"ft-2zaA7qi0rxJduWQpdvOvmGn3\", \n", + " model=\"davinci:ft-personal:coversation-users-full-kaskada-2023-08-05-14-25-30\", \n", " prompt=format_prompt(conversation),\n", + " logprobs=5,\n", " max_tokens=1,\n", + " stop=\" end\",\n", " temperature=0,\n", - " logprobs=5,\n", " )\n", "\n", " users = []\n", " logprobs = res[\"choices\"][0][\"logprobs\"][\"top_logprobs\"][0]\n", " for user in logprobs:\n", " if math.exp(logprobs[user]) > min_prob_for_response:\n", - " # if `nil` user is an option, stop processing\n", + " user = users.strip()\n", + " # if users include `nil`, stop processing\n", " if user == \"nil\":\n", " users = []\n", " break\n", @@ -263,18 +270,23 @@ " for user in users:\n", " user_id = le.inverse_transform(user)\n", "\n", + " # get user channel for slackbot\n", + " app = slack.web_client.users_conversations(\n", + " types=\"im\",\n", + " user=user_id,\n", + " )\n", + " \n", + " # confirm user has slackbot installed\n", + " if len(app[\"channels\"]) == 0:\n", + " continue\n", + "\n", " link = slack.web_client.chat_getPermalink(\n", " channel=msg[\"channel\"],\n", " message_ts=msg[\"ts\"],\n", " )[\"permalink\"]\n", " \n", - " app_channel = slack.web_client.users_conversations(\n", - " types=\"im\",\n", - " user=user_id,\n", - " )[\"channels\"][0][\"id\"]\n", - " \n", " slack.web_client.chat_postMessage(\n", - " channel=app_channel,\n", + " channel=app[\"channels\"][0][\"id\"],\n", " text=f'You may be interested in this converstation: <{link}|{msg[\"text\"]}>'\n", " )" ] diff --git a/examples/slackbot/slackbot.py b/examples/slackbot/slackbot.py index 9867e729f..094b6d68d 100644 --- a/examples/slackbot/slackbot.py +++ b/examples/slackbot/slackbot.py @@ -1,9 +1,8 @@ -from slack_sdk.socket_mode import SocketModeClient, SocketModeResponse +import json, math, openai, os, pyarrow +from slack_sdk.web import WebClient +from slack_sdk.socket_mode import SocketModeClient +from slack_sdk.socket_mode.response import SocketModeResponse import sparrow_pi as kt -import openai -import getpass -import pyarrow -import math def build_conversation(messages): message_time = messages.col("ts") @@ -12,8 +11,6 @@ def build_conversation(messages): return messages \ .select("user", "ts", "text", "reactions") \ - .collect(window=kt.windows.Since(is_new_conversation), max=100) \ - .select("user", "ts", "text") \ .collect(window=kt.windows.Since(is_new_conversation), max=100) def build_examples(messages): @@ -29,20 +26,30 @@ def build_examples(messages): return kt.record({ "prompt": shifted_coversation, "completion": engaged_users}) \ .filter(shifted_coversation.is_not_null()) +def format_prompt(prompt): + return "start -> " + "\n\n".join([f' {msg["user"]} --> {msg["text"]} ' for msg in prompt]) + "\n\n###\n\n" + def main(): + output_map = {} + + with open('./user_output_map.json', 'r') as file: + output_map = json.load(file) + + print(f'Loaded output map: {output_map}') + # Initialize Kaskada with a local execution context. kt.init_session() # Initialize OpenAI - openai.api_key = getpass.getpass('OpenAI: API Key') + openai.api_key = os.environ.get("OPEN_AI_KEY") # Initialize Slack slack = SocketModeClient( - app_token=getpass.getpass('Slack: App Token'), - web_client=getpass.getpass('Slack: Bot Token'), + app_token=os.environ.get("SLACK_APP_TOKEN"), + web_client=WebClient(token=os.environ.get("SLACK_BOT_TOKEN")) ) - min_prob_for_response = 0.75 + min_prob_for_response = 0.50 # Receive Slack messages in real-time live_messages = kt.sources.read_stream(entity_column="channel", time_column="ts") @@ -51,9 +58,21 @@ def main(): def handle_message(client, req): # Acknowledge the message back to Slack client.send_socket_mode_response(SocketModeResponse(envelope_id=req.envelope_id)) - - # Deliver the message to Kaskada - live_messages.add_event(pyarrow.json.read_json(req.payload)) + + if req.type == "events_api" and "event" in req.payload: + e = req.payload["event"] + + print(f'Received event from slack websocket: {e}') + + # ignore message edit, delete, reaction events + if "previous_message" in e or e["type"] == "reaction_added": + return + + print(f'Sending message event to kaskada: {e}') + + # Deliver the message to Kaskada + live_messages.add_event(pyarrow.json.read_json(e)) + slack.socket_mode_request_listeners.append(handle_message) slack.connect() @@ -62,47 +81,77 @@ def handle_message(client, req): for conversation in build_conversation(live_messages).start().to_generator(): if len(conversation) == 0: continue - + + print(f'Starting completion on conversation with first message text: {conversation[0]["text"]}') + + prompt = format_prompt(conversation) + + print(f'Using prompt: {prompt}') + # Ask the model who should be notified res = openai.Completion.create( - model="ft-2zaA7qi0rxJduWQpdvOvmGn3", - prompt=format_prompt(conversation), + model="davinci:ft-personal:coversation-users-full-kaskada-2023-08-05-14-25-30", + prompt=prompt, + logprobs=5, max_tokens=1, + stop=" end", temperature=0, - logprobs=5, ) + print(f'Received completion response: {res}') + users = [] logprobs = res["choices"][0]["logprobs"]["top_logprobs"][0] + + print(f'Found logprobs: {logprobs}') for user in logprobs: if math.exp(logprobs[user]) > min_prob_for_response: - # if `nil` user is an option, stop processing + user = users.strip() + # if users include `nil`, stop processing if user == "nil": users = [] break users.append(user) + print(f'Found users to alert: {users}') + # alert on most recent message in conversation msg = conversation.pop() - + # Send notification to users - for user in users: - user_id = le.inverse_transform(user) + for user_num in users: + if user_num not in output_map: + print(f'User: {user_num} not in output_map, stopping.') + continue + + user_id = output_map[user_num] + + print(f'Found user {user_num} in output map: {user_id}') + + app = slack.web_client.users_conversations( + types="im", + user=user_id, + ) + if len(app["channels"]) == 0: + print(f'User: {user_id} hasn\'t installed the slackbot yet') + continue + + app_channel = app["channels"][0]["id"] + print(f'Got user\'s slackbot channel id: {app_channel}') link = slack.web_client.chat_getPermalink( channel=msg["channel"], message_ts=msg["ts"], )["permalink"] - - app_channel = slack.web_client.users_conversations( - types="im", - user=user_id, - )["channels"][0]["id"] - + + print(f'Got message link: {link}') + slack.web_client.chat_postMessage( channel=app_channel, text=f'You may be interested in this converstation: <{link}|{msg["text"]}>' ) + print(f'Posted alert message') + if __name__ == "__main__": main() \ No newline at end of file diff --git a/examples/slackbot/user_output_map.json b/examples/slackbot/user_output_map.json new file mode 100644 index 000000000..c87cbe9f6 --- /dev/null +++ b/examples/slackbot/user_output_map.json @@ -0,0 +1 @@ +{"1": "U052Y3ZKGQJ", "2": "U052XUMJF6F", "4": "U052Y3Y23BL", "5": "U052RFMBRF0", "6": "U053MQ05DHN", "8": "U052RFM1QK0", "9": "U053AN5E281", "10": "U052HH8KE0P", "11": "U052RFHJ8EA", "13": "U0530HTBG2Y"} \ No newline at end of file