aammari commited on
Commit
b27a9ef
1 Parent(s): 759dead

ndis AI chatbot files

Browse files
Files changed (5) hide show
  1. __init__.py +0 -0
  2. app.py +128 -142
  3. data.json +298 -0
  4. requirements.txt +2 -6
  5. utils.py +171 -0
__init__.py ADDED
File without changes
app.py CHANGED
@@ -1,147 +1,133 @@
1
- import io
2
- import random
3
- from typing import List, Tuple
4
-
5
- import aiohttp
6
- import panel as pn
7
- from PIL import Image
8
- from transformers import CLIPModel, CLIPProcessor
9
-
10
- pn.extension(design="bootstrap", sizing_mode="stretch_width")
11
-
12
- ICON_URLS = {
13
- "brand-github": "https://github.com/holoviz/panel",
14
- "brand-twitter": "https://twitter.com/Panel_Org",
15
- "brand-linkedin": "https://www.linkedin.com/company/panel-org",
16
- "message-circle": "https://discourse.holoviz.org/",
17
- "brand-discord": "https://discord.gg/AXRHnJU6sP",
18
- }
19
-
20
-
21
- async def random_url(_):
22
- pet = random.choice(["cat", "dog"])
23
- api_url = f"https://api.the{pet}api.com/v1/images/search"
24
- async with aiohttp.ClientSession() as session:
25
- async with session.get(api_url) as resp:
26
- return (await resp.json())[0]["url"]
27
-
28
-
29
- @pn.cache
30
- def load_processor_model(
31
- processor_name: str, model_name: str
32
- ) -> Tuple[CLIPProcessor, CLIPModel]:
33
- processor = CLIPProcessor.from_pretrained(processor_name)
34
- model = CLIPModel.from_pretrained(model_name)
35
- return processor, model
36
-
37
-
38
- async def open_image_url(image_url: str) -> Image:
39
- async with aiohttp.ClientSession() as session:
40
- async with session.get(image_url) as resp:
41
- return Image.open(io.BytesIO(await resp.read()))
42
-
43
-
44
- def get_similarity_scores(class_items: List[str], image: Image) -> List[float]:
45
- processor, model = load_processor_model(
46
- "openai/clip-vit-base-patch32", "openai/clip-vit-base-patch32"
47
- )
48
- inputs = processor(
49
- text=class_items,
50
- images=[image],
51
- return_tensors="pt", # pytorch tensors
52
  )
53
- outputs = model(**inputs)
54
- logits_per_image = outputs.logits_per_image
55
- class_likelihoods = logits_per_image.softmax(dim=1).detach().numpy()
56
- return class_likelihoods[0]
 
 
 
 
 
 
 
 
 
 
 
 
57
 
 
 
 
58
 
59
- async def process_inputs(class_names: List[str], image_url: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  """
61
- High level function that takes in the user inputs and returns the
62
- classification results as panel objects.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  """
64
- try:
65
- main.disabled = True
66
- if not image_url:
67
- yield "##### ⚠️ Provide an image URL"
68
- return
69
-
70
- yield "##### ⚙ Fetching image and running model..."
71
- try:
72
- pil_img = await open_image_url(image_url)
73
- img = pn.pane.Image(pil_img, height=400, align="center")
74
- except Exception as e:
75
- yield f"##### 😔 Something went wrong, please try a different URL!"
76
- return
77
-
78
- class_items = class_names.split(",")
79
- class_likelihoods = get_similarity_scores(class_items, pil_img)
80
-
81
- # build the results column
82
- results = pn.Column("##### 🎉 Here are the results!", img)
83
-
84
- for class_item, class_likelihood in zip(class_items, class_likelihoods):
85
- row_label = pn.widgets.StaticText(
86
- name=class_item.strip(), value=f"{class_likelihood:.2%}", align="center"
87
- )
88
- row_bar = pn.indicators.Progress(
89
- value=int(class_likelihood * 100),
90
- sizing_mode="stretch_width",
91
- bar_color="secondary",
92
- margin=(0, 10),
93
- design=pn.theme.Material,
94
- )
95
- results.append(pn.Column(row_label, row_bar))
96
- yield results
97
- finally:
98
- main.disabled = False
99
-
100
-
101
- # create widgets
102
- randomize_url = pn.widgets.Button(name="Randomize URL", align="end")
103
-
104
- image_url = pn.widgets.TextInput(
105
- name="Image URL to classify",
106
- value=pn.bind(random_url, randomize_url),
107
- )
108
- class_names = pn.widgets.TextInput(
109
- name="Comma separated class names",
110
- placeholder="Enter possible class names, e.g. cat, dog",
111
- value="cat, dog, parrot",
112
- )
113
-
114
- input_widgets = pn.Column(
115
- "##### 😊 Click randomize or paste a URL to start classifying!",
116
- pn.Row(image_url, randomize_url),
117
- class_names,
118
- )
119
-
120
- # add interactivity
121
- interactive_result = pn.panel(
122
- pn.bind(process_inputs, image_url=image_url, class_names=class_names),
123
- height=600,
124
- )
125
-
126
- # add footer
127
- footer_row = pn.Row(pn.Spacer(), align="center")
128
- for icon, url in ICON_URLS.items():
129
- href_button = pn.widgets.Button(icon=icon, width=35, height=35)
130
- href_button.js_on_click(code=f"window.open('{url}')")
131
- footer_row.append(href_button)
132
- footer_row.append(pn.Spacer())
133
-
134
- # create dashboard
135
- main = pn.WidgetBox(
136
- input_widgets,
137
- interactive_result,
138
- footer_row,
139
- )
140
-
141
- title = "Panel Demo - Image Classification"
142
- pn.template.BootstrapTemplate(
143
- title=title,
144
- main=main,
145
- main_max_width="min(50%, 698px)",
146
- header_background="#F08080",
147
- ).servable(title=title)
 
1
+ import os
2
+ import openai
3
+ import sys
4
+ sys.path.append('../..')
5
+ import utils
6
+ import panel as pn # GUI
7
+ pn.extension(template='bootstrap')
8
+
9
+ deployment_name = os.environ['DEPLOYMENT_ID']
10
+
11
+ def get_completion_from_messages(messages, engine=deployment_name, temperature=0, max_tokens=500):
12
+ openai.api_key = os.environ['API_KEY']
13
+ openai.api_base = os.environ['API_BASE']
14
+ openai.api_type = os.environ['API_TYPE']
15
+ openai.api_version = os.environ['API_VERSION']
16
+ response = openai.ChatCompletion.create(
17
+ engine=engine,
18
+ messages=messages,
19
+ temperature=temperature,
20
+ max_tokens=max_tokens,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  )
22
+ return response.choices[0].message["content"]
23
+
24
+ def get_moderation_from_input(user_input):
25
+ openai.api_key = os.environ['MOD_API_KEY']
26
+ openai.api_base = os.environ['MOD_API_BASE']
27
+ openai.api_type = "open_ai"
28
+ openai.api_version = None
29
+ response = openai.Moderation.create(input=user_input)
30
+ return response
31
+
32
+ def process_user_message(user_input, all_messages, debug=True):
33
+ delimiter = "```"
34
+
35
+ # Step 1: Check input to see if it flags the Moderation API or is a prompt injection
36
+ response = get_moderation_from_input(user_input)
37
+ moderation_output = response["results"][0]
38
 
39
+ if moderation_output["flagged"]:
40
+ print("Step 1: Input flagged by Moderation API.")
41
+ return "Sorry, we cannot process this request."
42
 
43
+ if debug: print("Step 1: Input passed moderation check.")
44
+
45
+ page_and_qm_response = utils.find_pages_and_qms_only(user_input)
46
+ #print(print(page_and_qm_response)
47
+ # Step 2: Extract the list of products
48
+ page_and_qm_list = utils.read_string_to_list(page_and_qm_response)
49
+ #print(page_and_qm_list)
50
+
51
+ if debug: print("Step 2: Extracted list of quality markers.")
52
+
53
+ # Step 3: If quality markers are found, look them up
54
+ qm_information = utils.generate_output_string(page_and_qm_list)
55
+ if debug: print("Step 3: Looked up quality marker information.")
56
+
57
+ # Step 4: Answer the user question
58
+ system_message = f"""
59
+ You are an experienced assistant in the domain of Behaviour Support Plans (BSPs). \
60
+ Respond in a friendly and helpful tone, with concise answers. \
61
+ Your response MUST be ONLY based on the relevant information provided to you.
62
+ Make sure to ask the user relevant follow-up questions.
63
  """
64
+ messages = [
65
+ {'role': 'system', 'content': system_message},
66
+ {'role': 'user', 'content': f"{delimiter}{user_input}{delimiter}"},
67
+ {'role': 'assistant', 'content': f"Relevant information:\n{qm_information}"}
68
+ ]
69
+
70
+ final_response = get_completion_from_messages(all_messages + messages)
71
+ if debug:print("Step 4: Generated response to user question.")
72
+ all_messages = all_messages + messages[1:]
73
+
74
+ # Step 5: Put the answer through the Moderation API
75
+ response = get_moderation_from_input(final_response)
76
+ moderation_output = response["results"][0]
77
+
78
+ if moderation_output["flagged"]:
79
+ if debug: print("Step 5: Response flagged by Moderation API.")
80
+ return "Sorry, we cannot provide this information."
81
+
82
+ if debug: print("Step 5: Response passed moderation check.")
83
+
84
+ # Step 6: Ask the model if the response answers the initial user query well
85
+ user_message = f"""
86
+ Customer message: {delimiter}{user_input}{delimiter}
87
+ Agent response: {delimiter}{final_response}{delimiter}
88
+
89
+ Does the agent response sufficiently answer the customer question? \
90
+ Respond with a Y or N character, with no punctuation: \
91
+ Y - if the output sufficiently answers the question \
92
+ AND the response correctly uses the relevant information provided to the agent \
93
+ N - otherwise \
94
+ Output a single letter only.
95
  """
96
+ messages = [
97
+ {'role': 'system', 'content': system_message},
98
+ {'role': 'user', 'content': user_message}
99
+ ]
100
+ evaluation_response = get_completion_from_messages(messages)
101
+ if debug: print("Step 6: Model evaluated the response.")
102
+
103
+ # Step 7: If yes, use this answer; if not, say that you will connect the user to a human
104
+ if "Y" in evaluation_response: # Using "in" instead of "==" to be safer for model output variation (e.g., "Y." or "Yes")
105
+ if debug: print("Step 7: Model approved the response.")
106
+ return final_response, all_messages
107
+ else:
108
+ if debug: print("Step 7: Model disapproved the response.")
109
+ neg_str = "I'm unable to provide the information you're looking for. Please try asking again."
110
+ return neg_str, all_messages
111
+
112
+ chat_box = pn.widgets.ChatBox(value=[
113
+ {"You": ""},
114
+ {"AI Assistant": "Greetings! Feel free to ask about the BSP summary document, such as pages, quality markers, AI models, and NLP topics."}
115
+ ])
116
+ context = [{"role": "system", "content": "You are Service Assistant"}]
117
+
118
+ # Function that collects user and assistant messages over time
119
+ def collect_messages(event) -> None:
120
+ global context
121
+ user_input = event.new[-1]
122
+ user_message = user_input.get("You")
123
+ if user_message is None or user_message == "":
124
+ return
125
+ response, context = process_user_message(user_message, context, False)
126
+ context.append({"role": "assistant", "content": f"{response}"})
127
+ chat_box.append({"AI Assistant": response})
128
+
129
+ # Chat with the chatbot!
130
+ chat_box.param.watch(collect_messages, 'value')
131
+
132
+ dashboard = pn.Column(pn.pane.Markdown("# BSP Summary Document AI Assistant"), chat_box)
133
+ dashboard.servable()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data.json ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Q1": {
3
+ "page": "1",
4
+ "domain": "1",
5
+ "model_ids": [1, 2, 3, 4],
6
+ "topics": ["Age", "Gender", "Family History", "Diagnosed Disabilities", "Health Info", "Communication", "Likes", "Dislikes", "Sensory Experiences", "Goals & Aspirations"],
7
+ "text": "A summary description of the person who is the focus of the plan has been provided.",
8
+ "name": "Q1"
9
+ },
10
+ "Q2a": {
11
+ "page": "2",
12
+ "domain": "2",
13
+ "model_ids": [23, 31],
14
+ "topics": ["Person of Focus", "Exclusion Reason"],
15
+ "text": "The plan indicates that the person who is the focus of this plan was included in the assessment and data gathering processes OR an explanation has been provided as to why they were not included.",
16
+ "name": "Q2a"
17
+ },
18
+ "Q2b": {
19
+ "page": "2",
20
+ "domain": "2",
21
+ "model_ids": [5, 23, 25],
22
+ "topics": ["Stakeholder Interview", "File Review", "Observation / Communication", "Speak to Family", "Stakeholder Involvement", "Data Collectors", "Data Collected"],
23
+ "text": "The plan indicates that the focus person\u2019s family members, support staff, support providers and/or other professionals were included in the assessment and data gathering process.",
24
+ "name": "Q2b"
25
+ },
26
+ "Q2c": {
27
+ "page": "2",
28
+ "domain": "2",
29
+ "model_ids": [5, 7],
30
+ "topics": ["Stakeholder Interview", "File Review", "Observation / Communication", "Speak to Family", "Direct"],
31
+ "text": "The plan indicates that at least one direct data collection approach has been undertaken.",
32
+ "name": "Q2c"
33
+ },
34
+ "Q2d": {
35
+ "page": "2",
36
+ "domain": "2",
37
+ "model_ids": [6, 7],
38
+ "topics": ["Psychiatric Assessment", "Medical Assessment", "Speech and Language Assessment", "Indirect"],
39
+ "text": "The plan indicates that at least one indirect data collection approach has been undertaken.",
40
+ "name": "Q2d"
41
+ },
42
+ "Q3a": {
43
+ "page": "3",
44
+ "domain": "3",
45
+ "model_ids": [8],
46
+ "topics": ["Behaviour", "Glossary"],
47
+ "text": "All listed target behaviours are described in a way that would allow another person to act them out.",
48
+ "name": "Q3a"
49
+ },
50
+ "Q3b": {
51
+ "page": "3",
52
+ "domain": "3",
53
+ "model_ids": [8],
54
+ "topics": ["Frequency"],
55
+ "text": "Information has been provided about how often all the listed target behaviours occur (e.g., hourly, daily, weekly).",
56
+ "name": "Q3b"
57
+ },
58
+ "Q3c": {
59
+ "page": "3",
60
+ "domain": "3",
61
+ "model_ids": [8],
62
+ "topics": ["Duration"],
63
+ "text": "Information has been provided about how long all the listed target behaviours last for (e.g., seconds, minutes, hours, days).",
64
+ "name": "Q3c"
65
+ },
66
+ "Q3d": {
67
+ "page": "3",
68
+ "domain": "3",
69
+ "model_ids": [8],
70
+ "topics": ["Severity"],
71
+ "text": "Information has been provided about the severity of all the listed target behaviours (e.g., damage, disruption, injury, etc.).",
72
+ "name": "Q3d"
73
+ },
74
+ "Q3e": {
75
+ "page": "3",
76
+ "domain": "3",
77
+ "model_ids": [17],
78
+ "topics": ["Physical Signs", "Verbal Signs"],
79
+ "text": "All listed early warning signs are described in a way that is observable to others.",
80
+ "name": "Q3e"
81
+ },
82
+ "Q4a": {
83
+ "page": "3",
84
+ "domain": "4",
85
+ "model_ids": [9],
86
+ "topics": ["Triggers"],
87
+ "text": "The plan describes what is happening immediately before the target behaviour(s) are displayed, i.e., all identified triggers/antecedents are observable to others.",
88
+ "name": "Q4a"
89
+ },
90
+ "Q4b": {
91
+ "page": "3",
92
+ "domain": "4",
93
+ "model_ids": [9],
94
+ "topics": ["Consequences"],
95
+ "text": "The plan describes what happens immediately after the target behaviour(s) are displayed, i.e., all maintaining consequences are observable to others.",
96
+ "name": "Q4b"
97
+ },
98
+ "Q5b": {
99
+ "page": "3",
100
+ "domain": "5",
101
+ "model_ids": [12],
102
+ "topics": ["Background", "Contrubuting", "Sustaining", "Strength"],
103
+ "text": "The plan contains a formulation.",
104
+ "name": "Q5b"
105
+ },
106
+ "Q6b": {
107
+ "page": "4",
108
+ "domain": "6",
109
+ "model_ids": [15, 16],
110
+ "topics": ["Specific", "Measurable", "Achievable", "Relevant", "Timely", "Physical / Mental", "Relationships", "Personal Fulfillment", "Financial Stability", "Purpose / Impact"],
111
+ "text": "The plan contains at least one goal/objective that is related to the focus person\u2019s quality of life.",
112
+ "name": "Q6b"
113
+ },
114
+ "Q6c": {
115
+ "page": "4",
116
+ "domain": "6",
117
+ "model_ids": [15, 16],
118
+ "topics": ["Specific", "Measurable", "Achievable", "Relevant", "Timely", "Physical / Mental", "Relationships", "Personal Fulfillment", "Financial Stability", "Purpose / Impact"],
119
+ "text": "All goals are specific about who the goal/objective is for, what is to be achieved, when it will be achieved by.",
120
+ "name": "Q6c"
121
+ },
122
+ "Q7i": {
123
+ "page": "4",
124
+ "domain": "7",
125
+ "model_ids": [28],
126
+ "topics": ["Likes / Dislikes", "Interests", "Hobbies", "Relationships", "Employment", "Health", "Education"],
127
+ "text": "The plan specifies strategies that aim to enhance the focus person\u2019s quality of life and are not specifically related to the focus person\u2019s target behaviour(s).",
128
+ "name": "Q7i"
129
+ },
130
+ "Q7ii": {
131
+ "page": "4",
132
+ "domain": "7",
133
+ "model_ids": [14, 19],
134
+ "topics": ["Safety Strategy", "Setting Event Strategy", "Trigger Strategy", "Other Strategy"],
135
+ "text": "The plan specifies environmental strategies that address the identified setting events and/or triggers/antecedents.",
136
+ "name": "Q7ii"
137
+ },
138
+ "Q7iii": {
139
+ "page": "4",
140
+ "domain": "7",
141
+ "model_ids": [13, 19],
142
+ "topics": ["Replacement Behaviour", "Other Strategy"],
143
+ "text": "The plan specifies other strategies that will impact upon the focus person\u2019s target behaviour(s).",
144
+ "name": "Q7iii"
145
+ },
146
+ "Q7iv": {
147
+ "page": "4",
148
+ "domain": "7",
149
+ "model_ids": [27, 30],
150
+ "topics": ["Restrictive Intervention", "Physical Restraint", "Punitive Approaches"],
151
+ "text": "None of the proposed strategies reflect a restrictive practice (e.g., seclusion, restraint).",
152
+ "name": "Q7iv"
153
+ },
154
+ "Q7a": {
155
+ "page": "4",
156
+ "domain": "7",
157
+ "model_ids": [19, 14, 21, 29],
158
+ "topics": ["Safety Strategy", "Setting Event Strategy", "Trigger Strategy", "Other Strategy", "Privileges", "Breaks", "Verbal Praise", "Social Interaction", "Physical Rewards", "Attention", "When to Implement"],
159
+ "text": "All strategies are described in enough detail for someone to understand what needs to be done and when it needs to be implemented.",
160
+ "name": "Q7a"
161
+ },
162
+ "Q7b": {
163
+ "page": "4",
164
+ "domain": "7",
165
+ "model_ids": [13, 20, 21, 32],
166
+ "topics": ["Replacement Behaviour", "What", "How", "Who", "When", "Where", "Materials", "Privileges", "Breaks", "Verbal Praise", "Social Interaction", "Physical Rewards", "Attention", "Unneeded Teaching Reason"],
167
+ "text": "Does the plan propose to teach the person of focus alternative behaviours, functionally equivalent replacement behaviours and/or new skills?",
168
+ "name": "Q7b"
169
+ },
170
+ "Q8a": {
171
+ "page": "4",
172
+ "domain": "8",
173
+ "model_ids": [38],
174
+ "topics": ["Early Warning"],
175
+ "text": "A set of early warning signs for the target behaviour(s) are identified for the purposes of de-escalation.",
176
+ "name": "Q8a"
177
+ },
178
+ "Q8b": {
179
+ "page": "4",
180
+ "domain": "8",
181
+ "model_ids": [38],
182
+ "topics": ["Early Warning"],
183
+ "text": "At least one strategy relates to providing support to the person of focus when they start exhibiting the identified early warning signs.",
184
+ "name": "Q8b"
185
+ },
186
+ "Q8c": {
187
+ "page": "4",
188
+ "domain": "8",
189
+ "model_ids": [13],
190
+ "topics": ["Replacement Behaviour"],
191
+ "text": "At least one strategy relates to the person of focus being prompted to use an alternative response (e.g., a functionally equivalent behaviour, an alternative behaviour, moving onto another activity) when the target behaviour occurs.",
192
+ "name": "Q8c"
193
+ },
194
+ "Q8d": {
195
+ "page": "4",
196
+ "domain": "8",
197
+ "model_ids": [14],
198
+ "topics": ["Safety Strategy"],
199
+ "text": "At least one strategy relates to ensuring the safety of the person of focus and/or the people around them when the target behaviour(s) occur.",
200
+ "name": "Q8d"
201
+ },
202
+ "Q8e": {
203
+ "page": "4",
204
+ "domain": "8",
205
+ "model_ids": [18],
206
+ "topics": ["Debriefing Strategy"],
207
+ "text": "At least one strategy relates to debriefing with the person of focus and/or other relevant stakeholders at a designated time after the target behaviour(s) has subsided.",
208
+ "name": "Q8e"
209
+ },
210
+ "Q8f": {
211
+ "page": "4",
212
+ "domain": "8",
213
+ "model_ids": [27, 30],
214
+ "topics": ["Restrictive Intervention", "Physical Restraint", "Punitive Approaches"],
215
+ "text": "Restrictive interventions (e.g., seclusion, restraint) are not mentioned as a de-escalation strategy OR restrictive interventions are only discussed as a last resort strategy once all other de-escalation strategies have been implemented.",
216
+ "name": "Q8f"
217
+ },
218
+ "Q9a": {
219
+ "page": "5",
220
+ "domain": "9",
221
+ "model_ids": [5, 23, 25],
222
+ "topics": ["Stakeholder Interview", "Stakeholder Involvement", "Outcome Measurement Strategies"],
223
+ "text": "The plan describes at least one method of assessing the social validity of the proposed strategies (e.g., interview, rating scale).",
224
+ "name": "Q9a"
225
+ },
226
+ "Q9b": {
227
+ "page": "5",
228
+ "domain": "9",
229
+ "model_ids": [23, 31],
230
+ "topics": ["Person of Focus", "Exclusion Reason"],
231
+ "text": "The plan indicates that the focus person was consulted regarding the social validity of the proposed strategies OR an explanation has been provided around why they were not consulted.",
232
+ "name": "Q9b"
233
+ },
234
+ "Q9c": {
235
+ "page": "5",
236
+ "domain": "9",
237
+ "model_ids": [5, 23],
238
+ "topics": ["Stakeholder Interview", "Stakeholder Involvement", "Speak to Family"],
239
+ "text": "The plan indicates that the focus person\u2019s family members, support staff, support providers and/or other professionals were consulted regarding the social validity of the proposed strategies.",
240
+ "name": "Q9c"
241
+ },
242
+ "Q10a": {
243
+ "page": "5",
244
+ "domain": "10",
245
+ "model_ids": [24],
246
+ "topics": ["Implementors & Roles"],
247
+ "text": "At least one plan implementor and their role (e.g., parent, support staff) is identified in the plan.",
248
+ "name": "Q10a"
249
+ },
250
+ "Q10b": {
251
+ "page": "5",
252
+ "domain": "10",
253
+ "model_ids": [24],
254
+ "topics": ["Training Strategies"],
255
+ "text": "At least one strategy to train relevant plan implementors is proposed and indicates who will deliver the training.",
256
+ "name": "Q10b"
257
+ },
258
+ "Q10c": {
259
+ "page": "5",
260
+ "domain": "10",
261
+ "model_ids": [24],
262
+ "topics": ["Implementation Support"],
263
+ "text": "At least one strategy to support implementation in relevant settings (e.g., mentoring) is proposed and indicates who will deliver the strategy.",
264
+ "name": "Q10c"
265
+ },
266
+ "Q10d": {
267
+ "page": "5",
268
+ "domain": "10",
269
+ "model_ids": [24],
270
+ "topics": ["Communication Strategies"],
271
+ "text": "At least one strategy is proposed regarding how plan implementors will communicate with one another, relevant information about the plan and/or its implementation.",
272
+ "name": "Q10d"
273
+ },
274
+ "Q11a": {
275
+ "page": "5",
276
+ "domain": "11",
277
+ "model_ids": [25],
278
+ "topics": ["Outcome Measurement Strategies", "Data Collectors", "Data Collected"],
279
+ "text": "At least one outcome measurement strategy is proposed and indicates who will collect the data and what data will be collected.",
280
+ "name": "Q11a"
281
+ },
282
+ "Q11b": {
283
+ "page": "5",
284
+ "domain": "11",
285
+ "model_ids": [26],
286
+ "topics": ["Data Reviewer", "Reported To"],
287
+ "text": "The plan indicates who will review the data collected and who they will report the collated data to.",
288
+ "name": "Q11b"
289
+ },
290
+ "Q11c": {
291
+ "page": "5",
292
+ "domain": "11",
293
+ "model_ids": [26],
294
+ "topics": ["Review Meeting", "Review Timeframe"],
295
+ "text": "The plan provides a specific date or timeframe indicating when the plan will be reviewed.",
296
+ "name": "Q11c"
297
+ }
298
+ }
requirements.txt CHANGED
@@ -1,6 +1,2 @@
1
- panel
2
- jupyter
3
- transformers
4
- numpy
5
- torch
6
- aiohttp
 
1
+ openai
2
+ panel
 
 
 
 
utils.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import openai
4
+ from collections import defaultdict
5
+
6
+ deployment_name = "gpt-35-turbo"
7
+
8
+ data_file = 'data.json'
9
+
10
+ delimiter = "####"
11
+
12
+ def get_completion_from_messages(messages, engine=deployment_name, temperature=0, max_tokens=500):
13
+ openai.api_key = os.environ['API_KEY']
14
+ openai.api_base = os.environ['API_BASE']
15
+ openai.api_type = os.environ['API_TYPE']
16
+ openai.api_version = os.environ['API_VERSION']
17
+ response = openai.ChatCompletion.create(
18
+ engine=engine,
19
+ messages=messages,
20
+ temperature=temperature,
21
+ max_tokens=max_tokens,
22
+ )
23
+ return response.choices[0].message["content"]
24
+
25
+ def get_qms_and_pages():
26
+ qms = get_qms()
27
+ qms_by_page = defaultdict(list)
28
+ for qm_name, qm_info in qms.items():
29
+ page = qm_info.get('page')
30
+ if page:
31
+ qms_by_page[page].append(qm_name)
32
+
33
+ return dict(qms_by_page)
34
+
35
+ def get_qms():
36
+ with open(data_file, 'r') as file:
37
+ qms = json.load(file)
38
+ return qms
39
+
40
+ def find_pages_and_qms_only(user_input):
41
+ delimiter = "####"
42
+ example_string = """
43
+ Customer query: What topics are related to quality markers Q10c and Q5b? \
44
+ Your response: [{'page': '3', 'quality_marker': ['Q5b']}, {'page': '5', 'quality_marker': ['Q10c']}] \
45
+ Customer query: What topics are related to page 2? \
46
+ Your response: [{'page': '2', 'quality_marker': ['Q2a', 'Q2b', 'Q2c', 'Q2d']}]
47
+ """
48
+ system_message = f"""
49
+ You will be provided with a customer query. \
50
+ The customer query will be delimited with {delimiter} characters. \
51
+ The customer is expected to query about some details related to pages or quality markers in Behaviour Support Plan (PBS) summary documents. \
52
+ For example, the customer may want to know which domains, models, topics, or descriptions are associated with a given page or quality marker. \
53
+ Your task is to output a python list of objects (dicts), where each object has the following keys: \
54
+ - 'page' \
55
+ - 'quality_marker' \
56
+
57
+ Rules: \
58
+ - 'page' value must be one of: 1, 2, 3, 4, 5. \
59
+ - 'quality_marker' value is a list of allowed quality markers \
60
+ - If a specific page number is mentioned, it must be associated with all the correct quality markers in the allowed quality markers list below. \
61
+ - If specific quality marker(s) are mentioned, the 'quality_marker' list in your response must only include the mentioned quality marker(s). \
62
+ - If no pages or quality markers are found, output an empty list. \
63
+ - Only output the list of objects (dicts), nothing else.
64
+
65
+ Allowed quality markers:
66
+ For page 1:
67
+ Q1
68
+
69
+ For page 2:
70
+ Q2a
71
+ Q2b
72
+ Q2c
73
+ Q2d
74
+
75
+ For page 3:
76
+ Q3a
77
+ Q3b
78
+ Q3c
79
+ Q3d
80
+ Q3e
81
+ Q4a
82
+ Q4b
83
+ Q5b
84
+
85
+ For page 4:
86
+ Q6b
87
+ Q6c
88
+ Q7i
89
+ Q7ii
90
+ Q7iii
91
+ Q7iv
92
+ Q7a
93
+ Q7b
94
+ Q8a
95
+ Q8b
96
+ Q8c
97
+ Q8d
98
+ Q8e
99
+ Q8f
100
+
101
+ For page 5:
102
+ Q9a
103
+ Q9b
104
+ Q9c
105
+ Q10a
106
+ Q10b
107
+ Q10c
108
+ Q10d
109
+ Q11a
110
+ Q11b
111
+ Q11c
112
+
113
+ Examples:
114
+ {example_string}
115
+
116
+ """
117
+
118
+ messages = [
119
+ {'role':'system', 'content': system_message},
120
+ {'role':'user', 'content': f"{delimiter}{user_input}{delimiter}"},
121
+ ]
122
+ return get_completion_from_messages(messages)
123
+
124
+ # qm look up (either by page or by qm within page)
125
+ def get_qm_by_name(name):
126
+ qms = get_qms()
127
+ return qms.get(name, None)
128
+
129
+ def get_qms_by_page(page):
130
+ qms = get_qms()
131
+ return [qm for qm in qms.values() if qm.get('page') == page]
132
+
133
+ def read_string_to_list(input_string):
134
+ if input_string is None:
135
+ return None
136
+
137
+ try:
138
+ input_string = input_string.replace("'", "\"") # Replace single quotes with double quotes for valid JSON
139
+ data = json.loads(input_string)
140
+ return data
141
+ except json.JSONDecodeError:
142
+ print("Error: Invalid JSON string")
143
+ return None
144
+
145
+ def generate_output_string(data_list):
146
+ output_string = ""
147
+
148
+ if data_list is None:
149
+ return output_string
150
+
151
+ for data in data_list:
152
+ try:
153
+ if "quality_marker" in data.keys():
154
+ qm_list = data["quality_marker"]
155
+ for qm_name in qm_list:
156
+ qm = get_qm_by_name(qm_name)
157
+ if qm:
158
+ output_string += json.dumps(qm, indent=4) + "\n"
159
+ else:
160
+ print(f"Error: Quality marker '{qm_name}' not found")
161
+ elif "page" in data:
162
+ page_no = data["page"]
163
+ page_qms = get_qms_by_page(page_no)
164
+ for qm in page_qms:
165
+ output_string += json.dumps(qm, indent=4) + "\n"
166
+ else:
167
+ print("Error: Invalid object format")
168
+ except Exception as e:
169
+ print(f"Error: {e}")
170
+
171
+ return output_string