Skip to content

Commit

Permalink
updated slackbot script (#631)
Browse files Browse the repository at this point in the history
* minor updates to notebook version, left the `le =
preprocessing.LabelEncoder()` in there.
* lots of updates to the script version, including:
* swapping the labelEncoder for a json map lookup, which converts user
numbers in the model training set to Kaskada.AI slack userIds
* lots of `print()` debug statements that will be helpful to get this
going. we can remove them pre-demo.
  * getting tokens/api-keys from the env

also added the json data for the output lookup.

changes are based off this script:
[examples/slackbot/run.py](main...esp/try_script#diff-2067ff038f2b64f5ce70205cd7274f93552230391a57fc6ea05cc2ff05ffe8f5)
which was working successfully yesterday, when sending single messages
from slack to completion.
  • Loading branch information
epinzur authored Aug 9, 2023
1 parent 6279fe3 commit 568465f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 41 deletions.
38 changes: 25 additions & 13 deletions examples/slackbot/Notebook.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
" )"
]
Expand Down
105 changes: 77 additions & 28 deletions examples/slackbot/slackbot.py
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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):
Expand All @@ -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")
Expand All @@ -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()

Expand All @@ -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()
1 change: 1 addition & 0 deletions examples/slackbot/user_output_map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"1": "U052Y3ZKGQJ", "2": "U052XUMJF6F", "4": "U052Y3Y23BL", "5": "U052RFMBRF0", "6": "U053MQ05DHN", "8": "U052RFM1QK0", "9": "U053AN5E281", "10": "U052HH8KE0P", "11": "U052RFHJ8EA", "13": "U0530HTBG2Y"}

0 comments on commit 568465f

Please sign in to comment.