File size: 6,246 Bytes
5963990
caeec0f
 
 
 
 
 
 
 
 
 
 
6f97851
 
 
caeec0f
6f97851
caeec0f
 
 
5963990
 
 
caeec0f
 
5963990
 
 
 
 
 
 
 
 
 
caeec0f
 
 
 
6f97851
caeec0f
 
 
 
6f97851
caeec0f
 
 
 
 
 
 
 
0620e44
 
 
caeec0f
 
 
 
 
6f97851
 
caeec0f
 
 
6f97851
caeec0f
 
5963990
caeec0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15054f3
 
 
 
 
 
 
 
 
 
 
 
2ba773a
15054f3
 
 
caeec0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15054f3
 
 
caeec0f
0cc8f63
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from nltk.metrics.distance import edit_distance
import openai
import os
import questionnaires

initial_message = "Hi! I'm Brainy, a virtual assistant who can help you through the mental health screening process. Can you start by telling me a bit about the problems you are experiencing?"
services = "https://www.canada.ca/en/public-health/topics/mental-health-wellness.html"

openai.api_key = os.getenv("OPENAI_API_KEY")

available_questionnaires = list(questionnaires.topics)

def listify(options: list, end_and=True, lower=True, use_format=None):
    options = options.copy()
    if end_and is True and len(options) > 1: options[-1] = "and " + options[-1]
    if lower is True: options = list(map(str.lower, options))
    if use_format: list(map(use_format.format, options))
    return ", ".join(options) if len(options) > 2 else " ".join(options)

def content_summarizer():
    """
    Summarize previous messages for new prompts to pass as context.
    """
    pass

def response_aligner(options:list, response:str): 
    """
    Takes a series of options, and a response as given by Openai.
    Align the response to the nearest option (edit distance).
    This is necessary as sometimes openai will return a response slightly different to the ones in prompt.
    """
    distances = [(option, edit_distance(option, response)) for option in options]
    distances = sorted(distances, key=lambda item: item[1])
    return distances[0][0]

def assess_response(question: str, response:str, options: list, uses_scale=True, context="a psychological evaluation"):
    context_prompt = "" if context=="" else f"The context of this discussion is {context}. "
    if uses_scale is True:
        prompt = context_prompt + f"""
        Given the question '{question}' and this response to the question '{response}', rate the response on the following scale: {listify(options, end_and=False, lower=False, use_format="'{}'")}. 
        Return only the corresponding scale item and nothing else.
        """
    else:
        prompt = context_prompt + f"""
        Given the question '{question}' and this response to the question '{response}', which of the following is the response most similar to? {listify(options, end_and=False, lower=False, use_format="'{}'")}. 
        Return only the most similar item and nothing else.
        """
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=prompt,
        temperature=0.1,
        max_tokens=10
    )
    response = response.choices[0].text.strip()
    if response not in options: response = response_aligner(options, response)
    return response

def yes_no(message):
    return assess_response(question="Yes or No?", response=message, options=["Yes", "No"], context="", uses_scale=False)

def questionnaire_chooser(message):
    options = available_questionnaires + ["Suicidal", "None of the previously mentioned options"]

    topic = assess_response(
        question = initial_message,
        response = message,
        options = options,
        uses_scale = False
        )
    
    match topic.lower():
        case "suicidal":
            state = ["initial"]
            response = f"""
                It sounds like you are in a crisis, or require immediate assistance. If this is the case, please call 911.
                If would like to know more, resources can be found here: {services}.
                """
        case "none of the previously mentioned options": 
            state = ["initial"]
            response = f"""
                At the moment I can only help with {listify(available_questionnaires)}.
                If you are in a crisis, or require immediate assistance, please call 911.
                If you are looking for help with another mental health topic, more resources can be found here: {services}.
                """
        case _:
            state = ["potential", topic.lower()]
            response = f"It sounds like you're having trouble with {topic.lower()}. Is that correct?" 
    
    return state, response

def get_question(questionnaire, number):
    return questionnaires.questions[questionnaire][number]

def empath_response(message, next_message):
    prompt = f"""
        You a therapist talking to a patient about their mental health issues.
        They have just told you {message}.
        You are about to say {next_message} to them.
        Before you do so, say something empathetic in one or two sentences."""
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=prompt,
        temperature=1,
        max_tokens=30
    )
    response = response.choices[0].text.strip().rstrip(".")
    return response

def generate_response(user, message, empath=True):
    
    user_id, state = user["user_id"], user["state"]

    match state[0]:    
        case "initial":
            state, response = questionnaire_chooser(message)
        
        case "potential":
            if yes_no(message) == "Yes": 
                state = ["screening", state[1], 1]
                response = get_question(state[1], 0)
            else:
                state, response = ["initial"], "I think I misunderstood you. Let's try again... can you please tell me about the problems you are experiencing?"

        case "screening":
            state, question = state, get_question(state[1], state[2])
            
            if question == questionnaires.END:
                state = ["finished"]
                response = f"I have finished my evaluation, and that is all I can do for you today. Please refresh my page if you would like to start again. If you would like to give your clinician access to my assessment, please give them reference number #{user_id}."
            else: 
                state[2] += 1
                response = question

        case "finished":
            state, response = state, ""

    if empath is True: response = f"{empath_response(message, next_message=response)}. " + response

    return state, response

if __name__ == '__main__':
    user = {"user_id": 0, "state": ["initial"]}
    while True:
        message = input("YOU: ")
        user["state"], response = generate_response(user, message)
        print("BRAINY: ", response)