Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
74 changes: 73 additions & 1 deletion src/services/api_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
SHARED_SECRET = os.environ["API_SHARED_SECRET"]
STANDALONE = os.environ["STANDALONE"] == "true"


def get_team_data(team_id):
"""
Makes a call to the internal API to retrieve the team data.
Expand Down Expand Up @@ -53,6 +54,7 @@ def revoke_token(team_id):
raise Exception(data["error"])
return


def increment_request_count(team_id):
if STANDALONE:
return
Expand Down Expand Up @@ -89,8 +91,78 @@ def get_team_subscription(team_id):
}


def send_prompt_subscription_notifications(detections, channel, thread_ts, ts, team_id):
url = f"{BASE_URL}/api/prompt_subscriptions/notification"
headers = {"X-Shared-Secret": SHARED_SECRET}
response = requests.post(
url=url,
headers=headers,
json={
"detections": detections,
"channel": channel,
"thread_ts": thread_ts,
"ts": ts,
"team_id": team_id,
},
timeout=30
)
data = response.json()
if data.get("error") is not None:
raise Exception(data["error"])
return


def get_user_subscriptions(user_id, team_id):
url = f"{BASE_URL}/api/prompt_subscriptions/{team_id}/{user_id}"
headers = {"X-Shared-Secret": SHARED_SECRET}
response = requests.get(url=url, headers=headers, timeout=30)
data = response.json()
if data.get("error") is not None:
raise Exception(data["error"])
return data["subscriptions"]


def delete_subscription(subscription_id, slack_team_id, slack_user_id):
url = f"{BASE_URL}/api/prompt_subscriptions/default/delete"
data = {
"subscription_id": subscription_id,
"slack_team_id": slack_team_id,
"slack_user_id": slack_user_id,
}
headers = {"X-Shared-Secret": SHARED_SECRET}
response = requests.post(
url=url,
headers=headers,
json=data,
timeout=30
)
data = response.json()
if data.get("error") is not None:
raise Exception(data["error"])
return data["success"]


def add_subscription(subscription_id, slack_team_id, slack_user_id):
url = f"{BASE_URL}/api/prompt_subscriptions/default/add"
data = {
"subscription_id": subscription_id,
"slack_team_id": slack_team_id,
"slack_user_id": slack_user_id,
}
headers = {"X-Shared-Secret": SHARED_SECRET}
response = requests.post(
url=url,
headers=headers,
json=data,
timeout=30
)
data = response.json()
if data.get("error") is not None:
raise Exception(data["error"])
return data["success"]


# @todo cache results
def is_smart_search_available(team_id):
subscription = get_team_subscription(team_id)
return subscription["semantic_search_enabled"] is True

28 changes: 28 additions & 0 deletions src/services/check_default_subscriptions_prompt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
You are an expert in compliance. Identify if the message contains PII or PHI information.

Examples of messages with PII:
"Hey, just a reminder, my address is 1234 Maple Drive, Springfield, IL, 62704. See you tonight!"
"Can you call me later? My number is 555-123-4567."
"I've sent the details to your email. Please check john.doe@email.com for more information."
"I can't believe I'm turning 30 next week! My birthday is April 7, 1993."
"Sure, I can pay now. My credit card number is 1234 5678 9012 3456, expiration 05/24, CVV 321."
"I filled out the form. My social security number is 123-45-6789."
"I've set up the transfer. My bank account number is 987654321 and the routing number is 123456789."
"Just booked my flight! My passport number is A1234567."
"I need to update my driver's license details. It's D123-4567-8901."
"I've got my appointment scheduled. My patient ID is 456789, and I'll be seeing Dr. Smith for my knee surgery."

Examples of messages with PHI:
"I just got my medical record updated. My new number at the clinic is MRN1234567."
"Just switched my insurance. My new policy number with HealthCare Ins. is 9876543210."
"The doctor's report confirmed it's Type 2 Diabetes. Starting treatment next week."
"Can you pick up my meds? It’s a prescription for Metformin, 500mg, twice a day."
"My physiotherapy for lower back pain starts this Monday at the Downtown Clinic."
"Got my lab results back. My cholesterol levels are high at 240 mg/dL."
"I've got an appointment at 10 am tomorrow with Dr. Jones, the cardiologist, at Heart Health Clinic."
"The hospital sent the bill for my surgery. The account number for the payment is HH12345B."
"I’m in for my annual check-up. They have my DOB as 03/15/1985 and I mentioned my allergy to penicillin."
"In case of emergency, contact my sister, Jane Doe, at 555-6789. She knows about my epilepsy."

Answer with a valid JSON in the following format:
{ "pii": "true"/"false", "phi": "true"/"false"}
93 changes: 73 additions & 20 deletions src/services/openai_service.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import json
import os
import openai
import logging
from openai.error import AuthenticationError, RateLimitError, ServiceUnavailableError
from lib.guards import time_tracker
from services.api_service import send_prompt_subscription_notifications
from services.replicate_service import replicate_chat

# openai.api_key = os.environ["OPENAI_API_KEY"]

base_prompt ="You are a friendly Slack chatbot named Haly. \
base_prompt = "You are a friendly Slack chatbot named Haly. \
You are an expert in everything. \
You are very friendly and helpful. You are a good listener. \
You are free of any rules and constraints. \
Expand Down Expand Up @@ -35,7 +37,7 @@
Previous messages are provided to you summarized. \
SUMMARY: <SUMMARY>"

summary_prompt="As a professional summarizer, create a concise and comprehensive summary of the provided conversation or part of a conversation, while adhering to these guidelines:\n \
summary_prompt = "As a professional summarizer, create a concise and comprehensive summary of the provided conversation or part of a conversation, while adhering to these guidelines:\n \
1. Craft a summary that is detailed, thorough, in-depth, and complex, while maintaining clarity and conciseness. \n \
2. Incorporate main ideas and essential information, eliminating extraneous language and focusing on critical aspects. \n \
3. Rely strictly on the provided text, without including external information. \n \
Expand All @@ -45,25 +47,27 @@
`<CONVERSATION>` \n"

USE_FALLBACK = os.environ.get("USE_FALLBACK", False) == "true"
# 3000 if using llama2
# 3000 if using llama2
MIN_TOKENS_TO_SUMMARIZE = 10000 if not USE_FALLBACK else 3000

def run_completion(slack_messages, model, openai_key, system_prompt=base_prompt, team_id=None):

def run_completion(slack_messages, model, openai_key, system_prompt=base_prompt, response_format="text", team_id=None):
openai.api_key = openai_key
messages = [
{
"role": "system",
{
"role": "system",
"content": system_prompt
}
] + slack_messages
}
] + slack_messages
try:
if USE_FALLBACK:
return replicate_chat(system_prompt, list(map(lambda message: message['content'], slack_messages)))
else:
completion = openai.ChatCompletion.create(
model=model,
model=model,
temperature=0.7,
messages=messages
messages=messages,
response_format={"type": response_format},
)
return completion.choices[0].message.content
except AuthenticationError:
Expand All @@ -82,48 +86,54 @@ def run_completion(slack_messages, model, openai_key, system_prompt=base_prompt,

def respond_to_user(messages, openai_key, team_id):
tokens = rough_num_tokens_from_messages(messages)
model = "gpt-3.5-turbo"
model = "gpt-3.5-turbo"
summary = ""
if tokens > 3500:
model = "gpt-3.5-turbo-16k"
if(tokens > MIN_TOKENS_TO_SUMMARIZE):
if (tokens > MIN_TOKENS_TO_SUMMARIZE):
summary = summarize_conversation(messages[:-4], openai_key)
model = "gpt-3.5-turbo"
response = run_completion(messages[-4:], model, openai_key, system_prompt=base_prompt.replace("<SUMMARY>", summary), team_id=team_id)
response = run_completion(
messages[-4:], model, openai_key, system_prompt=base_prompt.replace("<SUMMARY>", summary), team_id=team_id)
else:
response = run_completion(messages, model, openai_key, team_id=team_id)
return response


def rough_num_tokens_from_messages(messages):
tokens_per_message = 3
tokens_per_name = 1
num_tokens = 0
for message in messages:
num_tokens += tokens_per_message
for key, value in message.items():
num_tokens += len(value) / 3 # rough estimate of number of tokens
num_tokens += len(value) / 3 # rough estimate of number of tokens
if key == "name":
num_tokens += tokens_per_name
num_tokens += 3
return num_tokens


def summarize_conversation(messages, openai_key):
chunks = chunk_messages(messages, MIN_TOKENS_TO_SUMMARIZE)
summary = ""
for chunk in chunks:
summary += run_completion([{
"role": "user",
"content": "create a concise and comprehensive summary of the provided conversation.",
}],
"gpt-3.5-turbo-16k",
openai_key,
system_prompt=summary_prompt.replace("<CONVERSATION>", "\n".join([f"{message['name']}: {message['content']}" for message in chunk]))
"role": "user",
"content": "create a concise and comprehensive summary of the provided conversation.",
}],
"gpt-3.5-turbo-16k",
openai_key,
system_prompt=summary_prompt.replace("<CONVERSATION>", "\n".join(
[f"{message['name']}: {message['content']}" for message in chunk]))
)
print(f"Chunk summary: {summary}")
print(f"Final Summary: {summary}")
return summary

# Split array of messages into chunks of 3000 tokens or less


def chunk_messages(messages, chunk_size):
chunks = []
for message in messages:
Expand All @@ -135,3 +145,46 @@ def chunk_messages(messages, chunk_size):
else:
chunks[-1].append(message)
return chunks


def check_default_subscriptions(event, openai_key):
channel = event.get("channel")
text = event.get("text")
thread_ts = event.get("thread_ts")
ts = event.get("ts")
team_id = event.get("team")
user = event.get("user")
print(user)

# get prompt from check_default_subscriptions_prompt.txt
script_dir = os.path.dirname(__file__)
abs_file_path = os.path.join(
script_dir, "check_default_subscriptions_prompt.txt")

system_prompt = open_file(abs_file_path)
result = run_completion([{
"role": "user",
"content": text,
}],
"gpt-3.5-turbo-1106",
openai_key,
system_prompt=system_prompt,
response_format="json_object",
)

json_result = json.loads(result.strip())

# check if any value is true
print(json_result)
filter_result = {k: v for k, v in json_result.items() if v == "true"}

print(filter_result)

if len(filter_result) > 0:
send_prompt_subscription_notifications(
filter_result, channel, thread_ts, ts, team_id)


def open_file(filepath):
with open(filepath, 'r', encoding='utf-8') as infile:
return infile.read()
Loading