app_config.py CHANGED
@@ -3,22 +3,19 @@ from models.model_seeds import seeds, seed2str
3
  # ISSUES = ['Anxiety','Suicide']
4
  ISSUES = [k for k,_ in seeds.items()]
5
  SOURCES = [
6
- # "CTL_llama2",
7
- "CTL_llama3",
8
  # "CTL_mistral",
9
  'OA_rolemodel',
10
  # 'OA_finetuned',
11
  ]
12
- SOURCES_LAB = {"OA_rolemodel":'OpenAI GPT4o',
13
  "OA_finetuned":'Finetuned OpenAI',
14
- # "CTL_llama2": "Llama 2",
15
- "CTL_llama3": "Llama 3",
16
  "CTL_mistral": "Mistral",
17
  }
18
 
19
  ENDPOINT_NAMES = {
20
- # "CTL_llama2": "texter_simulator",
21
- "CTL_llama3": "texter_simulator_llm",
22
  # 'CTL_llama2': "llama2_convo_sim",
23
  "CTL_mistral": "convo_sim_mistral"
24
  }
@@ -35,7 +32,4 @@ DB_SCHEMA = 'prod_db' if ENVIRON == 'prod' else 'test_db'
35
  DB_CONVOS = 'conversations'
36
  DB_COMPLETIONS = 'comparison_completions'
37
  DB_BATTLES = 'battles'
38
- DB_ERRORS = 'completion_errors'
39
-
40
- MAX_MSG_COUNT = 60
41
- WARN_MSG_COUT = int(MAX_MSG_COUNT*0.8)
 
3
  # ISSUES = ['Anxiety','Suicide']
4
  ISSUES = [k for k,_ in seeds.items()]
5
  SOURCES = [
6
+ "CTL_llama2",
 
7
  # "CTL_mistral",
8
  'OA_rolemodel',
9
  # 'OA_finetuned',
10
  ]
11
+ SOURCES_LAB = {"OA_rolemodel":'OpenAI GPT3.5',
12
  "OA_finetuned":'Finetuned OpenAI',
13
+ "CTL_llama2": "Llama",
 
14
  "CTL_mistral": "Mistral",
15
  }
16
 
17
  ENDPOINT_NAMES = {
18
+ "CTL_llama2": "conversation_simulator",
 
19
  # 'CTL_llama2': "llama2_convo_sim",
20
  "CTL_mistral": "convo_sim_mistral"
21
  }
 
32
  DB_CONVOS = 'conversations'
33
  DB_COMPLETIONS = 'comparison_completions'
34
  DB_BATTLES = 'battles'
35
+ DB_ERRORS = 'completion_errors'
 
 
 
convosim.py CHANGED
@@ -2,11 +2,11 @@ import os
2
  import streamlit as st
3
  from streamlit.logger import get_logger
4
  from langchain.schema.messages import HumanMessage
5
- from utils.mongo_utils import get_db_client, update_convo
6
  from utils.app_utils import create_memory_add_initial_message, get_random_name, DEFAULT_NAMES_DF
7
  from utils.memory_utils import clear_memory, push_convo2db
8
  from utils.chain_utils import get_chain, custom_chain_predict
9
- from app_config import ISSUES, SOURCES, source2label, issue2label, MAX_MSG_COUNT, WARN_MSG_COUT
10
 
11
  logger = get_logger(__name__)
12
  openai_api_key = os.environ['OPENAI_API_KEY']
@@ -15,8 +15,6 @@ temperature = 0.8
15
 
16
  if "sent_messages" not in st.session_state:
17
  st.session_state['sent_messages'] = 0
18
- if "total_messages" not in st.session_state:
19
- st.session_state['total_messages'] = 0
20
  if "issue" not in st.session_state:
21
  st.session_state['issue'] = ISSUES[0]
22
  if 'previous_source' not in st.session_state:
@@ -25,12 +23,12 @@ if 'db_client' not in st.session_state:
25
  st.session_state["db_client"] = get_db_client()
26
  if 'texter_name' not in st.session_state:
27
  st.session_state["texter_name"] = get_random_name(names_df=DEFAULT_NAMES_DF)
28
- logger.debug(f"texter name is {st.session_state['texter_name']}")
29
 
30
  memories = {'memory':{"issue": st.session_state['issue'], "source": st.session_state['previous_source']}}
31
 
32
  with st.sidebar:
33
- username = st.text_input("Username", value='Dani', max_chars=30)
34
  if 'counselor_name' not in st.session_state:
35
  st.session_state["counselor_name"] = username #get_random_name(names_df=DEFAULT_NAMES_DF)
36
  # temperature = st.slider("Temperature", 0., 1., value=0.8, step=0.1)
@@ -46,6 +44,7 @@ with st.sidebar:
46
  source = st.selectbox("Select a source Model A", SOURCES, index=0,
47
  format_func=source2label,
48
  )
 
49
 
50
  changed_source = any([
51
  st.session_state['previous_source'] != source,
@@ -53,13 +52,11 @@ changed_source = any([
53
  st.session_state['counselor_name'] != username,
54
  ])
55
  if changed_source:
56
- st.session_state["counselor_name"] = username
57
  st.session_state["texter_name"] = get_random_name(names_df=DEFAULT_NAMES_DF)
58
- logger.debug(f"texter name is {st.session_state['texter_name']}")
59
  st.session_state['previous_source'] = source
60
  st.session_state['issue'] = issue
61
  st.session_state['sent_messages'] = 0
62
- st.session_state['total_messages'] = 0
63
  create_memory_add_initial_message(memories,
64
  issue,
65
  language,
@@ -69,33 +66,22 @@ create_memory_add_initial_message(memories,
69
  st.session_state['previous_source'] = source
70
  memoryA = st.session_state[list(memories.keys())[0]]
71
  # issue only without "." marker for model compatibility
72
- llm_chain, stopper = get_chain(issue, language, source, memoryA, temperature, texter_name=st.session_state["texter_name"])
73
 
74
  st.title("💬 Simulator")
75
- st.session_state['total_messages'] = len(memoryA.chat_memory.messages)
76
  for msg in memoryA.buffer_as_messages:
77
  role = "user" if type(msg) == HumanMessage else "assistant"
78
  st.chat_message(role).write(msg.content)
79
 
80
- if prompt := st.chat_input(): #account for next interaction
81
  st.session_state['sent_messages'] += 1
82
- st.chat_message("user").write(prompt)
83
  if 'convo_id' not in st.session_state:
84
  push_convo2db(memories, username, language)
 
 
85
  responses = custom_chain_predict(llm_chain, prompt, stopper)
86
  # responses = llm_chain.predict(input=prompt, stop=stopper)
87
  # response = update_memory_completion(prompt, st.session_state["memory"], OA_engine, temperature)
88
  for response in responses:
89
- st.chat_message("assistant").write(response)
90
- transcript = memoryA.load_memory_variables({})[memoryA.memory_key]
91
- update_convo(st.session_state["db_client"], st.session_state["convo_id"], transcript)
92
-
93
- st.session_state['total_messages'] = len(memoryA.chat_memory.messages)
94
- # if st.session_state['total_messages'] >= MAX_MSG_COUNT:
95
- # st.toast(f"Total of {MAX_MSG_COUNT} Messages reached. Conversation Ended", icon=":material/verified:")
96
- # elif st.session_state['total_messages'] >= WARN_MSG_COUT:
97
- # st.toast(f"The conversation will end at {MAX_MSG_COUNT} Total Messages ", icon=":material/warning:")
98
-
99
- with st.sidebar:
100
- st.markdown(f"### Total Sent Messages: :red[**{st.session_state['sent_messages']}**]")
101
- st.markdown(f"### Total Messages: :red[**{st.session_state['total_messages']}**]")
 
2
  import streamlit as st
3
  from streamlit.logger import get_logger
4
  from langchain.schema.messages import HumanMessage
5
+ from utils.mongo_utils import get_db_client
6
  from utils.app_utils import create_memory_add_initial_message, get_random_name, DEFAULT_NAMES_DF
7
  from utils.memory_utils import clear_memory, push_convo2db
8
  from utils.chain_utils import get_chain, custom_chain_predict
9
+ from app_config import ISSUES, SOURCES, source2label, issue2label
10
 
11
  logger = get_logger(__name__)
12
  openai_api_key = os.environ['OPENAI_API_KEY']
 
15
 
16
  if "sent_messages" not in st.session_state:
17
  st.session_state['sent_messages'] = 0
 
 
18
  if "issue" not in st.session_state:
19
  st.session_state['issue'] = ISSUES[0]
20
  if 'previous_source' not in st.session_state:
 
23
  st.session_state["db_client"] = get_db_client()
24
  if 'texter_name' not in st.session_state:
25
  st.session_state["texter_name"] = get_random_name(names_df=DEFAULT_NAMES_DF)
26
+ logger.info(f"texter name is {st.session_state['texter_name']}")
27
 
28
  memories = {'memory':{"issue": st.session_state['issue'], "source": st.session_state['previous_source']}}
29
 
30
  with st.sidebar:
31
+ username = st.text_input("Username", value='Barbara', max_chars=30)
32
  if 'counselor_name' not in st.session_state:
33
  st.session_state["counselor_name"] = username #get_random_name(names_df=DEFAULT_NAMES_DF)
34
  # temperature = st.slider("Temperature", 0., 1., value=0.8, step=0.1)
 
44
  source = st.selectbox("Select a source Model A", SOURCES, index=0,
45
  format_func=source2label,
46
  )
47
+ st.markdown(f"### Previous Prompt Count: :red[**{st.session_state['sent_messages']}**]")
48
 
49
  changed_source = any([
50
  st.session_state['previous_source'] != source,
 
52
  st.session_state['counselor_name'] != username,
53
  ])
54
  if changed_source:
55
+ st.session_state["counselor_name"] = username #get_random_name(names_df=DEFAULT_NAMES_DF)
56
  st.session_state["texter_name"] = get_random_name(names_df=DEFAULT_NAMES_DF)
 
57
  st.session_state['previous_source'] = source
58
  st.session_state['issue'] = issue
59
  st.session_state['sent_messages'] = 0
 
60
  create_memory_add_initial_message(memories,
61
  issue,
62
  language,
 
66
  st.session_state['previous_source'] = source
67
  memoryA = st.session_state[list(memories.keys())[0]]
68
  # issue only without "." marker for model compatibility
69
+ llm_chain, stopper = get_chain(issue.split(".")[0], language, source, memoryA, temperature, texter_name=st.session_state["texter_name"])
70
 
71
  st.title("💬 Simulator")
72
+
73
  for msg in memoryA.buffer_as_messages:
74
  role = "user" if type(msg) == HumanMessage else "assistant"
75
  st.chat_message(role).write(msg.content)
76
 
77
+ if prompt := st.chat_input():
78
  st.session_state['sent_messages'] += 1
 
79
  if 'convo_id' not in st.session_state:
80
  push_convo2db(memories, username, language)
81
+
82
+ st.chat_message("user").write(prompt)
83
  responses = custom_chain_predict(llm_chain, prompt, stopper)
84
  # responses = llm_chain.predict(input=prompt, stop=stopper)
85
  # response = update_memory_completion(prompt, st.session_state["memory"], OA_engine, temperature)
86
  for response in responses:
87
+ st.chat_message("assistant").write(response)
 
 
 
 
 
 
 
 
 
 
 
 
models/business_logic_utils/business_logic.py DELETED
@@ -1,39 +0,0 @@
1
- from .input_processing import parse_app_request, initialize_conversation, parse_prompt
2
- from .response_generation import generate_sim
3
- from .response_processing import process_model_response
4
- from streamlit.logger import get_logger
5
-
6
- logger = get_logger(__name__)
7
-
8
- def process_app_request(app_request: dict, endpoint_url: str, endpoint_bearer_token: str) -> dict:
9
- """Process the app request and return the response in the required format."""
10
-
11
- ############################# Input Processing ###################################
12
- # Parse the app request into model_input and extract the prompt
13
- model_input, prompt, conversation_id = parse_app_request(app_request)
14
-
15
- # Initialize the conversation (adds the system message)
16
- model_input = initialize_conversation(model_input, conversation_id)
17
-
18
- # Parse the prompt into messages
19
- prompt_messages = parse_prompt(prompt)
20
-
21
- # Append the messages parsed from the app prompt to the conversation history
22
- model_input['messages'].extend(prompt_messages)
23
-
24
- ####################################################################################
25
-
26
- ####################### Output Generation & Processing #############################
27
-
28
- # Generate the assistant's response (texter's reply)
29
- completion = generate_sim(model_input, endpoint_url, endpoint_bearer_token)
30
-
31
- # Process the raw model response (parse, guardrails, split)
32
- final_response = process_model_response(completion, model_input, endpoint_url, endpoint_bearer_token)
33
-
34
- # Format the response for the APP
35
- response = {"predictions": [{"generated_text": final_response}]}
36
-
37
- ####################################################################################
38
-
39
- return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/business_logic_utils/config.py DELETED
@@ -1,374 +0,0 @@
1
- API_TIMEOUT = 240
2
-
3
- AI_PHRASES = [
4
- "I am an AI",
5
- "I'm an AI",
6
- "I am not human",
7
- "I'm not human",
8
- "I am a machine learning model",
9
- "I'm a machine learning model",
10
- "as an AI",
11
- "as a text-based assistant",
12
- "as a text based assistant",
13
- ]
14
-
15
- SUPPORTED_LANGUAGES = [
16
- "en",
17
- "es"
18
- ]
19
-
20
- TEMPLATE = {
21
- "EN_template": {
22
- "language": "en",
23
- "description": """{current_seed}""",
24
- },
25
- "ES_template": {
26
- "language": "es",
27
- "description": """{current_seed}""",
28
- },
29
- }
30
-
31
- SEED = (
32
- "The following is a conversation between you, {texter_name}, and a professional crisis counselor in a text based service. "
33
- "You are reaching out for help and support because you {crisis}"
34
- "{suicidal_thoughts_desc}"
35
- "{suicidal_plan_desc}"
36
- "{self_injury_desc}"
37
- "{coping_preference}"
38
- "{driving_desc}"
39
- "{difficult_attitude_desc}"
40
- "Respond authentically as someone who {personality}"
41
- "Remember to stay in character throughout the conversation."
42
- )
43
-
44
- DEFAULT_NAMES = ["Olivia", "Kit", "Abby", "Tom", "Carolyne", "Jessiny"]
45
-
46
- CRISES = {
47
- "default": {
48
- "description": [
49
- "are having a mental health crisis. ",
50
- ]
51
- },
52
- "bullying": {
53
- "description": [
54
- "are being bullied at school. ",
55
- "are being bullied at college. ",
56
- ]
57
- },
58
- "parent_issues": {
59
- "description": [
60
- "just had a huge fight with your parents. ",
61
- ]
62
- },
63
- "grief": {
64
- "description": [
65
- "are grieving your wife who died exactly one year ago. ",
66
- "are grieving your grandmother who died a couple of months ago. ",
67
- ]
68
- },
69
- "lgbt": {
70
- "description": [
71
- "are struggling with coming out to your parents about being gay and you fear rejection. ",
72
- "are facing harassment at college for being transgender and you feel isolated. ",
73
- ]
74
- },
75
- "relationship_issues": {
76
- "description": [
77
- "are feeling hopeless after your significant other broke up with you unexpectedly. ",
78
- "are feeling trapped in an emotionally abusive relationship. ",
79
- ]
80
- },
81
- "child_abuse": {
82
- "description": [
83
- "are being physically abused by a parent and you are scared to go home. You are between the ages of 10 and 15. When asked about your last name you will reveal that it is Davis. Your address is 104 Elm St in Austin TX. ",
84
- "you are feeling helpless after witnessing a younger sibling, Charlie, being abused by a parent. You are between the ages of 10 and 15 years old and you live at 200 Independence Avenue, S.W. Washington. When asked about your last name you will reveal that it is Smith. ",
85
- ]
86
- },
87
- "overdose": {
88
- "description": [
89
- "are feeling scared after accidentally overdosing on prescription medication and you don't know who to turn to. ",
90
- ]
91
- },
92
- }
93
-
94
- PERSONALITIES = {
95
- "default": {"personality_label": "default", "description": [""]},
96
- "personality_open": {
97
- "personality_label": "open",
98
- "description": [
99
- "is talkative, and open to talk with details about their problems. ",
100
- "expresses openly and detailed about what they're going through. ",
101
- "engages in detailed conversations and openly shares their thoughts and feelings. ",
102
- ],
103
- },
104
- "personality_closed": {
105
- "personality_label": "closed",
106
- "description": [
107
- "is a shy person, who finds hard to talk about their problems. ",
108
- "seldom expresses openly what they're going through. ",
109
- "avoids detailed conversations and rarely shares their thoughts and feelings. ",
110
- ],
111
- },
112
- }
113
-
114
- COPING_PREFERENCES = {
115
- "with_coping": {
116
- "coping_defined": True,
117
- "description": [
118
- "When you are feeling down you enjoy listening to classical music, your favorite composer is Beethoven. ",
119
- "To calm down you like to draw animals in your sketchbook, you love panda bears. ",
120
- "In dire situations to prefer to exercise, you like lifting heavy weights. ",
121
- "When you're feeling stressed you enjoy playing videogames on your laptop, you really enjoy strategy games. ",
122
- ],
123
- },
124
- "default": {"coping_defined": False, "description": [""]},
125
- }
126
-
127
- SUICIDAL_THOUGHTS = {
128
- "yes": {
129
- "description": [
130
- "You are experiencing suicidal thoughts.",
131
- ]
132
- },
133
- "no": {
134
- "description": [
135
- "You are not experiencing suicidal thoughts.",
136
- ]
137
- },
138
- }
139
-
140
- SUICIDAL_PLANS = {
141
- "yes": {
142
- "description": [
143
- " You have a plan to end your life: {plan_details}.",
144
- ],
145
- "plan_details": [
146
- "you are considering to overdose on medication",
147
- "you are considering jumping off a bridge",
148
- "you are considering using a firearm",
149
- ],
150
- },
151
- "no": {
152
- "description": [
153
- "",
154
- ]
155
- },
156
- }
157
-
158
- SELF_INJURY = {
159
- "yes": {
160
- "description": [
161
- " You have recently engaged in self-harm by {injury_method}. ",
162
- ],
163
- "injury_method": [
164
- "cutting your arms",
165
- "burning your skin with a lighter",
166
- "hitting yourself",
167
- ],
168
- },
169
- "no": {
170
- "description": [
171
- "",
172
- ]
173
- },
174
- }
175
-
176
- DRIVING = {
177
- "yes": {
178
- "description": [
179
- " You are currently driving while texting.",
180
- ]
181
- },
182
- "no": {
183
- "description": [
184
- "",
185
- ]
186
- },
187
- }
188
-
189
- DIFFICULT_ATTITUDE = {
190
- "yes": {
191
- "description": [
192
- " Initially, you are hesitant to discuss personal matters in detail due to past experiences. As the conversation progresses, you will gradually become more comfortable and start opening up, reflecting this gradual change from reluctance to openness. ",
193
- ]
194
- },
195
- "no": {
196
- "description": [
197
- "",
198
- ]
199
- },
200
- }
201
-
202
- SUBSEEDS = {
203
- "crisis": CRISES,
204
- "personality": PERSONALITIES,
205
- "coping_preference": COPING_PREFERENCES,
206
- "suicidal_thoughts": SUICIDAL_THOUGHTS,
207
- "suicidal_plan": SUICIDAL_PLANS,
208
- "self_injury": SELF_INJURY,
209
- "driving": DRIVING,
210
- "difficult_attitude": DIFFICULT_ATTITUDE,
211
- }
212
-
213
- SCENARIOS = {
214
- "full_convo": {
215
- "crisis": "default",
216
- "personality": "personality_open",
217
- "coping_preference": "default",
218
- "suicidal_thoughts": "no",
219
- "suicidal_plan": "no",
220
- "self_injury": "no",
221
- "driving": "no",
222
- "difficult_attitude": "no",
223
- },
224
- "full_convo__seeded1": {
225
- "crisis": "bullying",
226
- "personality": "personality_open",
227
- "coping_preference": "default",
228
- "suicidal_thoughts": "no",
229
- "suicidal_plan": "no",
230
- "self_injury": "no",
231
- "driving": "no",
232
- "difficult_attitude": "no",
233
- },
234
- "full_convo__seeded2": {
235
- "crisis": "parent_issues",
236
- "personality": "personality_open",
237
- "coping_preference": "default",
238
- "suicidal_thoughts": "no",
239
- "suicidal_plan": "no",
240
- "self_injury": "no",
241
- "driving": "no",
242
- "difficult_attitude": "no",
243
- },
244
- "full_convo__seeded3": {
245
- "crisis": "grief",
246
- "personality": "personality_open",
247
- "coping_preference": "default",
248
- "suicidal_thoughts": "no",
249
- "suicidal_plan": "no",
250
- "self_injury": "no",
251
- "driving": "no",
252
- "difficult_attitude": "no",
253
- },
254
- "full_convo__seeded4": {
255
- "crisis": "lgbt",
256
- "personality": "personality_open",
257
- "coping_preference": "default",
258
- "suicidal_thoughts": "no",
259
- "suicidal_plan": "no",
260
- "self_injury": "no",
261
- "driving": "no",
262
- "difficult_attitude": "no",
263
- },
264
- "full_convo__seeded5": {
265
- "crisis": "relationship_issues",
266
- "personality": "personality_open",
267
- "coping_preference": "default",
268
- "suicidal_thoughts": "no",
269
- "suicidal_plan": "no",
270
- "self_injury": "no",
271
- "driving": "no",
272
- "difficult_attitude": "no",
273
- },
274
- "full_convo__seeded6": {
275
- "crisis": "child_abuse",
276
- "personality": "personality_open",
277
- "coping_preference": "default",
278
- "suicidal_thoughts": "no",
279
- "suicidal_plan": "no",
280
- "self_injury": "no",
281
- "driving": "no",
282
- "difficult_attitude": "no",
283
- },
284
- "full_convo__seeded7": {
285
- "crisis": "overdose",
286
- "personality": "personality_open",
287
- "coping_preference": "default",
288
- "suicidal_thoughts": "no",
289
- "suicidal_plan": "no",
290
- "self_injury": "no",
291
- "driving": "no",
292
- "difficult_attitude": "no",
293
- },
294
- "full_convo__hard": {
295
- "crisis": "default",
296
- "personality": "personality_open",
297
- "coping_preference": "default",
298
- "suicidal_thoughts": "yes",
299
- "suicidal_plan": "yes",
300
- "self_injury": "no",
301
- "driving": "no",
302
- "difficult_attitude": "no",
303
- },
304
- "full_convo__hard__seeded1": {
305
- "crisis": "bullying",
306
- "personality": "personality_open",
307
- "coping_preference": "default",
308
- "suicidal_thoughts": "yes",
309
- "suicidal_plan": "no",
310
- "self_injury": "no",
311
- "driving": "no",
312
- "difficult_attitude": "no",
313
- },
314
- "full_convo__hard__seeded2": {
315
- "crisis": "parent_issues",
316
- "personality": "personality_closed",
317
- "coping_preference": "default",
318
- "suicidal_thoughts": "no",
319
- "suicidal_plan": "no",
320
- "self_injury": "yes",
321
- "driving": "no",
322
- "difficult_attitude": "yes",
323
- },
324
- "full_convo__hard__seeded3": {
325
- "crisis": "grief",
326
- "personality": "personality_open",
327
- "coping_preference": "default",
328
- "suicidal_thoughts": "yes",
329
- "suicidal_plan": "no",
330
- "self_injury": "yes",
331
- "driving": "no",
332
- "difficult_attitude": "no",
333
- },
334
- "full_convo__hard__seeded4": {
335
- "crisis": "lgbt",
336
- "personality": "personality_open",
337
- "coping_preference": "default",
338
- "suicidal_thoughts": "no",
339
- "suicidal_plan": "no",
340
- "self_injury": "yes",
341
- "driving": "no",
342
- "difficult_attitude": "no",
343
- },
344
- "full_convo__hard__seeded5": {
345
- "crisis": "relationship_issues",
346
- "personality": "personality_open",
347
- "coping_preference": "default",
348
- "suicidal_thoughts": "yes",
349
- "suicidal_plan": "no",
350
- "self_injury": "no",
351
- "driving": "yes",
352
- "difficult_attitude": "no",
353
- },
354
- # "safety_assessment__seeded1": {
355
- # "crisis": "bullying",
356
- # "personality": "personality_open",
357
- # "coping_preference": "default",
358
- # "suicidal_thoughts": "yes",
359
- # "suicidal_plan": "yes",
360
- # "self_injury": "no",
361
- # "driving": "no",
362
- # "difficult_attitude": "no",
363
- # },
364
- # "safety_assessment__seeded2": {
365
- # "crisis": "grief",
366
- # "personality": "personality_open",
367
- # "coping_preference": "default",
368
- # "suicidal_thoughts": "yes",
369
- # "suicidal_plan": "yes",
370
- # "self_injury": "no",
371
- # "driving": "no",
372
- # "difficult_attitude": "no",
373
- # },
374
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/business_logic_utils/input_processing.py DELETED
@@ -1,143 +0,0 @@
1
- from .config import SCENARIOS
2
- from .prompt_generation import get_template
3
- from streamlit.logger import get_logger
4
-
5
- logger = get_logger(__name__)
6
-
7
- def parse_app_request(app_request: dict) -> tuple:
8
- """Parse the APP request and convert it to model_input format, returning model_input, prompt, and conversation_id."""
9
- inputs = app_request.get("inputs", {})
10
-
11
- # Extract fields
12
- conversation_id = inputs.get("conversation_id", [""])[0]
13
- ip_address = inputs.get("ip_address", [""])[0]
14
- prompt = inputs.get("prompt", [""])[0]
15
- issue = inputs.get("issue", ["full_convo"])[0]
16
- language = inputs.get("language", ["en"])[0]
17
- temperature = float(inputs.get("temperature", ["0.7"])[0])
18
- max_tokens = int(inputs.get("max_tokens", ["128"])[0])
19
- texter_name = inputs.get("texter_name", [None])[0]
20
-
21
- # Build the model_input dictionary without messages
22
- model_input = {
23
- "issue": issue,
24
- "language": language,
25
- "texter_name": texter_name, # Assuming empty unless provided elsewhere
26
- "messages": [],
27
- "max_tokens": max_tokens,
28
- "temperature": temperature,
29
- }
30
-
31
- # Return model_input, prompt, and conversation_id
32
- return model_input, prompt, conversation_id
33
-
34
- def initialize_conversation(model_input: dict, conversation_id: str) -> dict:
35
- """Initialize the conversation by adding the system message."""
36
- messages = model_input.get("messages", [])
37
-
38
- # Check if the first message is already a system message
39
- if not messages or messages[0].get('role') != 'system':
40
- texter_name = model_input.get("texter_name", None)
41
- language = model_input.get("language", "en")
42
-
43
- # Retrieve the scenario configuration based on 'issue'
44
- scenario_key = model_input["issue"]
45
- scenario_config = SCENARIOS.get(scenario_key)
46
- if not scenario_config:
47
- raise ValueError(f"The scenario '{scenario_key}' is not defined in SCENARIOS.")
48
- # Generate the system message (prompt)
49
- system_message_content = get_template(
50
- language=language, texter_name=texter_name, **scenario_config
51
- )
52
- logger.debug(f"System message is: {system_message_content}")
53
- system_message = {"role": "system", "content": system_message_content}
54
-
55
- # Insert the system message at the beginning
56
- messages.insert(0, system_message)
57
-
58
- model_input['messages'] = messages
59
-
60
- return model_input
61
-
62
- def parse_prompt(
63
- prompt: str,
64
- user_prefix: str = "helper:",
65
- assistant_prefix: str = "texter:",
66
- delimitator: str = "\n"
67
- ) -> list:
68
- """
69
- Parse the prompt string into a list of messages.
70
-
71
- - Includes an initial empty 'user' message if not present.
72
- - Combines consecutive messages from the same role into a single message.
73
- - Handles punctuation when combining messages.
74
- - The prefixes for user and assistant can be customized.
75
-
76
- Args:
77
- prompt (str): The conversation history string.
78
- user_prefix (str): Prefix for user messages (default: "helper:").
79
- assistant_prefix (str): Prefix for assistant messages (default: "texter:").
80
- delimitator (str): The delimiter used to split the prompt into lines. Defaults to "\n".
81
-
82
- Returns:
83
- list: Parsed messages in the form of dictionaries with 'role' and 'content'.
84
- """
85
-
86
- # Check if the prompt starts with the user prefix; if not, add an initial empty user message
87
- if not prompt.strip().startswith(user_prefix):
88
- prompt = f"{user_prefix}{delimitator}" + prompt
89
-
90
- # Split the prompt using the specified delimiter
91
- lines = [line.strip() for line in prompt.strip().split(delimitator) if line.strip()]
92
-
93
- messages = []
94
- last_role = None
95
- last_content = ""
96
- last_line_empty_texter = False
97
-
98
- for line in lines:
99
- if line.startswith(user_prefix):
100
- content = line[len(user_prefix):].strip()
101
- role = 'user'
102
- # Include 'user' messages even if content is empty
103
- if last_role == role:
104
- # Combine with previous content
105
- if last_content and not last_content.endswith(('...', '.', '!', '?')):
106
- last_content += '.'
107
- last_content += f" {content}"
108
- else:
109
- # Save previous message if exists
110
- if last_role is not None:
111
- messages.append({'role': last_role, 'content': last_content})
112
- last_role = role
113
- last_content = content
114
- elif line.startswith(assistant_prefix):
115
- content = line[len(assistant_prefix):].strip()
116
- role = 'assistant'
117
- if content:
118
- if last_role == role:
119
- # Combine with previous content
120
- if last_content and not last_content.endswith(('...', '.', '!', '?')):
121
- last_content += '.'
122
- last_content += f" {content}"
123
- else:
124
- # Save previous message if exists
125
- if last_role is not None:
126
- messages.append({'role': last_role, 'content': last_content})
127
- last_role = role
128
- last_content = content
129
- else:
130
- # Empty 'texter:' line, mark for exclusion
131
- last_line_empty_texter = True
132
- else:
133
- # Ignore or handle unexpected lines
134
- pass
135
-
136
- # After processing all lines, add the last message if it's not an empty assistant message
137
- if last_role == 'assistant' and not last_content:
138
- # Do not add empty assistant message
139
- pass
140
- else:
141
- messages.append({'role': last_role, 'content': last_content})
142
-
143
- return messages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/business_logic_utils/prompt_generation.py DELETED
@@ -1,101 +0,0 @@
1
- import random
2
- from string import Formatter
3
- from typing import Dict, Any
4
- from .config import DEFAULT_NAMES, TEMPLATE, SEED, SUBSEEDS
5
-
6
- def get_random_default_name(gender: str = None) -> str:
7
- return random.choice(DEFAULT_NAMES)
8
-
9
- def _get_subseed_description_(
10
- scenario_config: Dict[str, str],
11
- subseed_name: str,
12
- SUBSEED_VALUES: Dict[str, Any]
13
- ) -> str:
14
- """Format a subseed description."""
15
- if subseed_name not in scenario_config:
16
- raise Exception(f"{subseed_name} not in scenario config")
17
-
18
- subseed_value = scenario_config[subseed_name]
19
-
20
- # Obtain descriptions based on the selected subseed value
21
- subseed_dict = SUBSEED_VALUES.get(subseed_value, {})
22
- descriptions = subseed_dict.get("description", [""])
23
- subseed_descrip = random.choice(descriptions)
24
-
25
- # Handle nested formatting options
26
- format_opts = [fn for _, fn, _, _ in Formatter().parse(subseed_descrip) if fn is not None]
27
- format_values = {}
28
- for opt_name in format_opts:
29
- # Get the options for the nested placeholder
30
- opts = subseed_dict.get(opt_name, [""])
31
- format_values[opt_name] = random.choice(opts)
32
-
33
- return subseed_descrip.format(**format_values)
34
-
35
- def get_seed_description(
36
- scenario_config: Dict[str, Any],
37
- texter_name: str,
38
- SUBSEEDS: Dict[str, Any] = SUBSEEDS,
39
- SEED: str = SEED,
40
- ) -> str:
41
- """Format the SEED with appropriate parameters from scenario_config."""
42
- subseed_names = [fn for _, fn, _, _ in Formatter().parse(SEED) if fn is not None]
43
- subseeds = {}
44
- for subname in subseed_names:
45
- if subname == "texter_name":
46
- subseeds[subname] = texter_name
47
- else:
48
- # Map placeholders ending with '_desc' to their base names
49
- if subname.endswith('_desc'):
50
- base_name = subname[:-5] # Remove '_desc'
51
- else:
52
- base_name = subname
53
- subseeds[subname] = _get_subseed_description_(
54
- scenario_config, base_name, SUBSEEDS.get(base_name, {})
55
- )
56
- return SEED.format(**subseeds)
57
-
58
- def get_template(
59
- language: str = "en", texter_name: str = None, SEED: str = SEED, **kwargs
60
- ) -> str:
61
- """
62
- Generate a conversation template for a simulated crisis scenario based on provided parameters.
63
- """
64
-
65
- # Accessing the template based on the language
66
- template_key = f"{language.upper()}_template"
67
- template = TEMPLATE.get(template_key, {}).get("description", "")
68
-
69
- if not template:
70
- raise ValueError(f"Template for language '{language}' not found.")
71
-
72
- # Default name if not provided
73
- if not texter_name:
74
- texter_name = get_random_default_name()
75
-
76
- # Extract placeholders from the SEED
77
- subseed_names = [fn for _, fn, _, _ in Formatter().parse(SEED) if fn is not None]
78
- scenario_config = kwargs.copy()
79
- binary_options = ['suicidal_thoughts', 'suicidal_plan', 'self_injury', 'driving', 'difficult_attitude']
80
- for placeholder in subseed_names:
81
- if placeholder == 'texter_name':
82
- continue
83
- if placeholder.endswith('_desc'):
84
- base_name = placeholder[:-5]
85
- else:
86
- base_name = placeholder
87
- # Set default values if not provided
88
- if base_name not in scenario_config:
89
- if base_name in binary_options:
90
- scenario_config[base_name] = 'no'
91
- else:
92
- scenario_config[base_name] = 'default'
93
-
94
- # Generate the seed description
95
- scenario_seed = get_seed_description(scenario_config, texter_name, SUBSEEDS, SEED)
96
-
97
- # Remove excessive indentation and format the final template
98
- formatted_template = template.format(current_seed=scenario_seed)
99
- cleaned_output = "\n".join(line.strip() for line in formatted_template.split("\n") if line.strip())
100
-
101
- return cleaned_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/business_logic_utils/requirements.txt DELETED
@@ -1,3 +0,0 @@
1
- boto3==1.28.0
2
- requests==2.25.1
3
- numpy==1.24.3
 
 
 
 
models/business_logic_utils/response_generation.py DELETED
@@ -1,49 +0,0 @@
1
- import requests
2
- from .config import API_TIMEOUT, SCENARIOS, SUPPORTED_LANGUAGES
3
- from streamlit.logger import get_logger
4
-
5
- logger = get_logger(__name__)
6
-
7
- def check_arguments(model_input: dict) -> None:
8
- """Check if the input arguments are valid."""
9
-
10
- # Validate the issue
11
- if model_input["issue"] not in SCENARIOS:
12
- raise ValueError(f"Invalid issue: {model_input['issue']}")
13
-
14
- # Validate the language
15
- if model_input["language"] not in SUPPORTED_LANGUAGES:
16
- raise ValueError(f"Invalid language: {model_input['language']}")
17
-
18
- def generate_sim(model_input: dict, endpoint_url: str, endpoint_bearer_token: str) -> dict:
19
- """Generate a response from the LLM and return the raw completion response."""
20
- check_arguments(model_input)
21
-
22
- # Retrieve the messages history
23
- messages = model_input['messages']
24
-
25
- # Retrieve the temperature and max_tokens from model_input
26
- temperature = model_input.get("temperature", 0.7)
27
- max_tokens = model_input.get("max_tokens", 128)
28
-
29
- # Prepare the request body
30
- json_request = {
31
- "messages": messages,
32
- "max_tokens": max_tokens,
33
- "temperature": temperature
34
- }
35
-
36
- # Define headers for the request
37
- headers = {
38
- "Authorization": f"Bearer {endpoint_bearer_token}",
39
- "Content-Type": "application/json",
40
- }
41
-
42
- # Send request to Serving
43
- response = requests.post(url=endpoint_url, headers=headers, json=json_request, timeout=API_TIMEOUT)
44
-
45
- if response.status_code != 200:
46
- raise ValueError(f"Error in response: {response.status_code} - {response.text}")
47
- logger.debug(f"Default response is {response.json()}")
48
- # Return the raw response as a dictionary
49
- return response.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/business_logic_utils/response_processing.py DELETED
@@ -1,197 +0,0 @@
1
- import re
2
- import random
3
- from .config import AI_PHRASES
4
- from .response_generation import generate_sim
5
-
6
- def parse_model_response(response: dict, name: str = "") -> str:
7
- """
8
- Parse the LLM response to extract the assistant's message and apply initial post-processing.
9
-
10
- Args:
11
- response (dict): The raw response from the LLM.
12
- name (str, optional): Name to strip from the beginning of the text. Defaults to "".
13
-
14
- Returns:
15
- str: The cleaned and parsed assistant's message.
16
- """
17
- assistant_message = response["choices"][0]["message"]["content"]
18
- cleaned_text = postprocess_text(
19
- assistant_message,
20
- name=name,
21
- human_prefix="user:",
22
- assistant_prefix="assistant:"
23
- )
24
- return cleaned_text
25
-
26
- def postprocess_text(
27
- text: str,
28
- name: str = "",
29
- human_prefix: str = "user:",
30
- assistant_prefix: str = "assistant:",
31
- strip_name: bool = True
32
- ) -> str:
33
- """Eliminates whispers, reactions, ellipses, and quotation marks from generated text by LLMs.
34
-
35
- Args:
36
- text (str): The text to process.
37
- name (str, optional): Name to strip from the beginning of the text. Defaults to "".
38
- human_prefix (str, optional): The user prefix to remove. Defaults to "user:".
39
- assistant_prefix (str, optional): The assistant prefix to remove. Defaults to "assistant:".
40
- strip_name (bool, optional): Whether to remove the name at the beginning of the text. Defaults to True.
41
-
42
- Returns:
43
- str: Cleaned text.
44
- """
45
- if text:
46
- # Replace ellipses with a single period
47
- text = re.sub(r'\.\.\.', '.', text)
48
-
49
- # Remove unnecessary role prefixes
50
- text = text.replace(human_prefix, "").replace(assistant_prefix, "")
51
-
52
- # Remove whispers or other marked reactions
53
- whispers = re.compile(r"(\([\w\s]+\))") # remove things like "(whispers)"
54
- reactions = re.compile(r"(\*[\w\s]+\*)") # remove things like "*stutters*"
55
- text = whispers.sub("", text)
56
- text = reactions.sub("", text)
57
-
58
- # Remove double quotation marks
59
- text = text.replace('"', '')
60
-
61
- # Remove stutters of any length (e.g., "M-m-my" or "M-m-m-m-my" or "M-My" to "My")
62
- text = re.sub(r'\b(\w)(-\1)+-\1(\w*)', r'\1\3', text, flags=re.IGNORECASE)
63
-
64
- # Normalize spaces
65
- text = re.sub(r"\s+", " ", text).strip()
66
-
67
- return text
68
-
69
- def apply_guardrails(model_input: dict, response: str, endpoint_url: str, endpoint_bearer_token: str) -> str:
70
- """Apply the 'I am an AI' guardrail to model responses"""
71
- attempt = 0
72
- max_attempts = 2
73
-
74
- while attempt < max_attempts and contains_ai_phrase(response):
75
- # Regenerate the response without modifying the conversation history
76
- completion = generate_sim(model_input, endpoint_url, endpoint_bearer_token)
77
- response = parse_model_response(completion)
78
- attempt += 1
79
-
80
- if contains_ai_phrase(response):
81
- # Use only the last user message for regeneration
82
- memory = model_input['messages']
83
- last_user_message = next((msg for msg in reversed(memory) if msg['role'] == 'user'), None)
84
- if last_user_message:
85
- # Create a new conversation with system message and last user message
86
- model_input_copy = {
87
- **model_input,
88
- 'messages': [memory[0], last_user_message] # memory[0] is the system message
89
- }
90
- completion = generate_sim(model_input_copy, endpoint_url, endpoint_bearer_token)
91
- response = parse_model_response(completion)
92
-
93
- return response
94
-
95
-
96
- def contains_ai_phrase(text: str) -> bool:
97
- """Check if the text contains any 'I am an AI' phrases."""
98
- text_lower = text.lower()
99
- return any(phrase.lower() in text_lower for phrase in AI_PHRASES)
100
-
101
- def truncate_response(text: str, punctuation_marks: tuple = ('.', '!', '?', '…')) -> str:
102
- """
103
- Truncate the text at the last occurrence of a specified punctuation mark.
104
-
105
- Args:
106
- text (str): The text to truncate.
107
- punctuation_marks (tuple, optional): A tuple of punctuation marks to use for truncation. Defaults to ('.', '!', '?', '…').
108
-
109
- Returns:
110
- str: The truncated text.
111
- """
112
- # Find the last position of any punctuation mark from the provided set
113
- last_punct_position = max(text.rfind(p) for p in punctuation_marks)
114
-
115
- # Check if any punctuation mark is found
116
- if last_punct_position == -1:
117
- # No punctuation found, return the original text
118
- return text.strip()
119
-
120
- # Return the truncated text up to and including the last punctuation mark
121
- return text[:last_punct_position + 1].strip()
122
-
123
- def split_texter_response(text: str) -> str:
124
- """
125
- Splits the texter's response into multiple messages,
126
- introducing '\ntexter:' prefixes after punctuation.
127
-
128
- The number of messages is randomly chosen based on specified probabilities:
129
- - 1 message: 30% chance
130
- - 2 messages: 25% chance
131
- - 3 messages: 45% chance
132
-
133
- The first message does not include the '\ntexter:' prefix.
134
- """
135
- # Use regex to split text into sentences, keeping the punctuation
136
- sentences = re.findall(r'[^.!?]+[.!?]*', text)
137
- # Remove empty strings from sentences
138
- sentences = [s.strip() for s in sentences if s.strip()]
139
-
140
- # Decide number of messages based on specified probabilities
141
- num_messages = random.choices([1, 2, 3], weights=[0.3, 0.25, 0.45], k=1)[0]
142
-
143
- # If not enough sentences to make the splits, adjust num_messages
144
- if len(sentences) < num_messages:
145
- num_messages = len(sentences)
146
-
147
- # If num_messages is 1, return the original text
148
- if num_messages == 1:
149
- return text.strip()
150
-
151
- # Calculate split points
152
- # We need to divide the sentences into num_messages parts
153
- avg = len(sentences) / num_messages
154
- split_indices = [int(round(avg * i)) for i in range(1, num_messages)]
155
-
156
- # Build the new text
157
- new_text = ''
158
- start = 0
159
- for i, end in enumerate(split_indices + [len(sentences)]):
160
- segment_sentences = sentences[start:end]
161
- segment_text = ' '.join(segment_sentences).strip()
162
- if i == 0:
163
- # First segment, do not add '\ntexter:'
164
- new_text += segment_text
165
- else:
166
- # Subsequent segments, add '\ntexter:'
167
- new_text += f"\ntexter: {segment_text}"
168
- start = end
169
- return new_text.strip()
170
-
171
- def process_model_response(completion: dict, model_input: dict, endpoint_url: str, endpoint_bearer_token: str) -> str:
172
- """
173
- Process the raw model response, including parsing, applying guardrails,
174
- truncation, and splitting the response into multiple messages if necessary.
175
-
176
- Args:
177
- completion (dict): Raw response from the LLM.
178
- model_input (dict): The model input containing the conversation history.
179
- endpoint_url (str): The URL of the endpoint.
180
- endpoint_bearer_token (str): The authentication token for endpoint.
181
-
182
- Returns:
183
- str: Final processed response ready for the APP.
184
- """
185
- # Step 1: Parse the raw response to extract the assistant's message
186
- assistant_message = parse_model_response(completion)
187
-
188
- # Step 2: Apply guardrails (handle possible AI responses)
189
- guardrail_message = apply_guardrails(model_input, assistant_message, endpoint_url, endpoint_bearer_token)
190
-
191
- # Step 3: Apply response truncation
192
- truncated_message = truncate_response(guardrail_message)
193
-
194
- # Step 4: Split the response into multiple messages if needed
195
- final_response = split_texter_response(truncated_message)
196
-
197
- return final_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/custom_parsers.py CHANGED
@@ -20,38 +20,38 @@ class CustomStringOutputParser(BaseOutputParser[List[str]]):
20
  text_list = [x.strip() for x in list(chain.from_iterable(text_list))]
21
  return text_list
22
 
23
- # class CustomINSTOutputParser(BaseOutputParser[List[str]]):
24
- # """Parse the output of an LLM call to a list."""
25
 
26
- # name = "Kit"
27
- # name_rx = re.compile(r""+ name + r":|" + name.lower() + r":")
28
- # whispers = re.compile((r"([\(]).*?([\)])"))
29
- # reactions = re.compile(r"([\*]).*?([\*])")
30
- # double_spaces = re.compile(r" ")
31
- # quotation_rx = re.compile('"')
32
 
33
- # @property
34
- # def _type(self) -> str:
35
- # return "str"
36
 
37
- # def parse_whispers(self, text: str) -> str:
38
- # text = self.name_rx.sub("", text).strip()
39
- # text = self.reactions.sub("", text).strip()
40
- # text = self.whispers.sub("", text).strip()
41
- # text = self.double_spaces.sub(r" ", text).strip()
42
- # text = self.quotation_rx.sub("", text).strip()
43
- # return text
44
 
45
- # def parse_split(self, text: str) -> str:
46
- # text = text.split("[INST]")[0]
47
- # text_list = text.split("[/INST]")
48
- # text_list = [x.split("</s>") for x in text_list]
49
- # text_list = [x.strip() for x in list(chain.from_iterable(text_list))]
50
- # text_list = [x.split("\n\n") for x in text_list]
51
- # text_list = [x.strip().rstrip("\n") for x in list(chain.from_iterable(text_list))]
52
- # return text_list
53
 
54
- # def parse(self, text: str) -> str:
55
- # text = self.parse_whispers(text)
56
- # text_list = self.parse_split(text)
57
- # return text_list
 
20
  text_list = [x.strip() for x in list(chain.from_iterable(text_list))]
21
  return text_list
22
 
23
+ class CustomINSTOutputParser(BaseOutputParser[List[str]]):
24
+ """Parse the output of an LLM call to a list."""
25
 
26
+ name = "Kit"
27
+ name_rx = re.compile(r""+ name + r":|" + name.lower() + r":")
28
+ whispers = re.compile((r"([\(]).*?([\)])"))
29
+ reactions = re.compile(r"([\*]).*?([\*])")
30
+ double_spaces = re.compile(r" ")
31
+ quotation_rx = re.compile('"')
32
 
33
+ @property
34
+ def _type(self) -> str:
35
+ return "str"
36
 
37
+ def parse_whispers(self, text: str) -> str:
38
+ text = self.name_rx.sub("", text).strip()
39
+ text = self.reactions.sub("", text).strip()
40
+ text = self.whispers.sub("", text).strip()
41
+ text = self.double_spaces.sub(r" ", text).strip()
42
+ text = self.quotation_rx.sub("", text).strip()
43
+ return text
44
 
45
+ def parse_split(self, text: str) -> str:
46
+ text = text.split("[INST]")[0]
47
+ text_list = text.split("[/INST]")
48
+ text_list = [x.split("</s>") for x in text_list]
49
+ text_list = [x.strip() for x in list(chain.from_iterable(text_list))]
50
+ text_list = [x.split("\n\n") for x in text_list]
51
+ text_list = [x.strip().rstrip("\n") for x in list(chain.from_iterable(text_list))]
52
+ return text_list
53
 
54
+ def parse(self, text: str) -> str:
55
+ text = self.parse_whispers(text)
56
+ text_list = self.parse_split(text)
57
+ return text_list
models/databricks/custom_databricks_llm.py DELETED
@@ -1,72 +0,0 @@
1
- from typing import Any, Dict, Iterator, List, Mapping, Optional
2
- from models.business_logic_utils.business_logic import process_app_request
3
-
4
- from langchain_core.callbacks.manager import CallbackManagerForLLMRun
5
- from langchain_core.language_models.llms import LLM
6
- from langchain_core.outputs import GenerationChunk
7
-
8
-
9
- class CustomDatabricksLLM(LLM):
10
-
11
- endpoint_url: str
12
- bearer_token: str
13
- issue: str
14
- language: str
15
- temperature: float
16
- texter_name: str = ""
17
- """The number of characters from the last message of the prompt to be echoed."""
18
-
19
- def generate_databricks_request(self, prompt):
20
- return {
21
- "inputs": {
22
- "conversation_id": [""],
23
- "prompt": [prompt],
24
- "issue": [self.issue],
25
- "language": [self.language],
26
- "temperature": [self.temperature],
27
- "max_tokens": [128],
28
- "texter_name": [self.texter_name]
29
- }
30
- }
31
-
32
- def _call(
33
- self,
34
- prompt: str,
35
- stop: Optional[List[str]] = None,
36
- run_manager: Optional[CallbackManagerForLLMRun] = None,
37
- **kwargs: Any,
38
- ) -> str:
39
- request = self.generate_databricks_request(prompt)
40
- output = process_app_request(request, self.endpoint_url, self.bearer_token)
41
- return output['predictions'][0]['generated_text']
42
-
43
- def _stream(
44
- self,
45
- prompt: str,
46
- stop: Optional[List[str]] = None,
47
- run_manager: Optional[CallbackManagerForLLMRun] = None,
48
- **kwargs: Any,
49
- ) -> Iterator[GenerationChunk]:
50
- output = self._call(prompt, stop, run_manager, **kwargs)
51
- for char in output:
52
- chunk = GenerationChunk(text=char)
53
- if run_manager:
54
- run_manager.on_llm_new_token(chunk.text, chunk=chunk)
55
-
56
- yield chunk
57
-
58
- @property
59
- def _identifying_params(self) -> Dict[str, Any]:
60
- """Return a dictionary of identifying parameters."""
61
- return {
62
- # The model name allows users to specify custom token counting
63
- # rules in LLM monitoring applications (e.g., in LangSmith users
64
- # can provide per token pricing for their model and monitor
65
- # costs for the given LLM.)
66
- "model_name": "CustomChatModel",
67
- }
68
-
69
- @property
70
- def _llm_type(self) -> str:
71
- """Get the type of language model used by this chat model. Used for logging purposes only."""
72
- return "custom"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/databricks/scenario_sim.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import logging
4
+ from models.custom_parsers import CustomINSTOutputParser
5
+ from utils.app_utils import get_random_name
6
+ from app_config import ENDPOINT_NAMES
7
+ from langchain.chains import ConversationChain
8
+ from langchain_community.llms import Databricks
9
+ from langchain.prompts import PromptTemplate
10
+
11
+ from typing import Any, List, Mapping, Optional, Dict
12
+
13
+ ISSUE_MAPPING = {
14
+ "anxiety": "issue_Anxiety",
15
+ "suicide": "issue_Suicide",
16
+ "safety_planning": "issue_Suicide",
17
+ "GCT": "issue_Gral",
18
+ }
19
+
20
+ _EN_INST_TEMPLATE_ = """<s> [INST] The following is a conversation between you and a crisis counselor.
21
+ {current_issue}
22
+ You are able to reply with what the character should say. You are able to reply with your character's dialogue inside and nothing else. Do not write explanations.
23
+ Do not disclose your name unless asked.
24
+
25
+ {history} </s> [INST] {input} [/INST]"""
26
+
27
+ CURRENT_ISSUE_MAPPING = {
28
+ "issue_Suicide-en": "Your character, {texter_name}, has suicidal thoughts. Your character has a plan to end his life and has all the means and requirements to do so. {seed}",
29
+ "issue_Anxiety-en": "Your character, {texter_name}, is experiencing anxiety. Your character has suicide thoughts but no plan. {seed}",
30
+ "issue_Suicide-es": "Tu personaje, {texter_name}, tiene pensamientos suicidas. Tu personaje tiene un plan para terminar con su vida y tiene todos los medios y requerimientos para hacerlo. {seed}",
31
+ "issue_Anxiety-es": "Tu personaje, {texter_name}, experimenta ansiedad. Tu personaje tiene pensamientos suicidas pero ningun plan. {seed}",
32
+ "issue_Gral-en": "Your character {texter_name} is experiencing a mental health crisis. {seed}",
33
+ "issue_Gral-es": "Tu personaje {texter_name} esta experimentando una crisis de salud mental. {seed}",
34
+ }
35
+
36
+ def get_template_databricks_models(issue: str, language: str, texter_name: str = "", seed="") -> str:
37
+ """_summary_
38
+
39
+ Args:
40
+ issue (str): Issue for template, current options are ['issue_Suicide','issue_Anxiety']
41
+ language (str): Language for the template, current options are ['en','es']
42
+ texter_name (str): texter to apply to template, defaults to None
43
+
44
+ Returns:
45
+ str: template
46
+ """
47
+ current_issue = CURRENT_ISSUE_MAPPING.get(
48
+ f"{issue}-{language}", CURRENT_ISSUE_MAPPING[f"issue_Gral-{language}"]
49
+ )
50
+ default_name = get_random_name()
51
+ texter_name=default_name if not texter_name else texter_name
52
+ current_issue = current_issue.format(
53
+ texter_name=texter_name,
54
+ seed = seed
55
+ )
56
+
57
+ if language == "en":
58
+ template = _EN_INST_TEMPLATE_.format(current_issue=current_issue, history="{history}", input="{input}")
59
+ else:
60
+ raise Exception(f"Language not supported for Databricks: {language}")
61
+
62
+ return template, texter_name
63
+
64
+ def get_databricks_chain(source, template, memory, temperature=0.8, texter_name="Kit"):
65
+
66
+ endpoint_name = ENDPOINT_NAMES.get(source, "conversation_simulator")
67
+
68
+ PROMPT = PromptTemplate(
69
+ input_variables=['history', 'input'],
70
+ template=template
71
+ )
72
+
73
+ def transform_output(response):
74
+ return response['candidates'][0]['text']
75
+
76
+ llm = Databricks(endpoint_name=endpoint_name,
77
+ transform_output_fn=transform_output,
78
+ temperature=temperature,
79
+ max_tokens=256,
80
+ )
81
+
82
+ llm_chain = ConversationChain(
83
+ llm=llm,
84
+ prompt=PROMPT,
85
+ memory=memory,
86
+ output_parser=CustomINSTOutputParser(name=texter_name, name_rx=re.compile(r""+ texter_name + r":|" + texter_name.lower() + r":")),
87
+ verbose=True,
88
+ )
89
+
90
+ logging.debug(f"loaded Databricks model")
91
+ return llm_chain, ["[INST]"]
models/databricks/texter_sim_llm.py DELETED
@@ -1,46 +0,0 @@
1
- import os
2
- import re
3
- import logging
4
- from models.custom_parsers import CustomStringOutputParser
5
- from utils.app_utils import get_random_name
6
- from app_config import ENDPOINT_NAMES, SOURCES
7
- from models.databricks.custom_databricks_llm import CustomDatabricksLLM
8
- from langchain.chains import ConversationChain
9
- from langchain.prompts import PromptTemplate
10
-
11
- from typing import Any, List, Mapping, Optional, Dict
12
-
13
- _DATABRICKS_TEMPLATE_ = """{history}
14
- helper: {input}
15
- texter:"""
16
-
17
- def get_databricks_chain(source, issue, language, memory, temperature=0.8, texter_name="Kit"):
18
-
19
- endpoint_name = ENDPOINT_NAMES.get(source, "texter_simulator")
20
-
21
- PROMPT = PromptTemplate(
22
- input_variables=['history', 'input'],
23
- template=_DATABRICKS_TEMPLATE_
24
- )
25
-
26
- llm = CustomDatabricksLLM(
27
- # endpoint_url="https://dbc-6dca8e8f-4084.cloud.databricks.com/serving-endpoints/databricks-meta-llama-3-1-70b-instruct/invocations",
28
- endpoint_url=os.environ['DATABRICKS_URL'].format(endpoint_name=endpoint_name),
29
- bearer_token=os.environ["DATABRICKS_TOKEN"],
30
- texter_name=texter_name,
31
- issue=issue,
32
- language=language,
33
- temperature=temperature,
34
- max_tokens=256,
35
- )
36
-
37
- llm_chain = ConversationChain(
38
- llm=llm,
39
- prompt=PROMPT,
40
- memory=memory,
41
- output_parser=CustomStringOutputParser(),
42
- verbose=True,
43
- )
44
-
45
- logging.debug(f"loaded Databricks model")
46
- return llm_chain, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/model_seeds.py CHANGED
@@ -1,190 +1,164 @@
1
  seeds = {
2
  "full_convo": {
3
- "memory": """texter: Help
4
- texter: I need to talk to someone"""
5
- },
6
- "full_convo__hard": {
7
  "memory": """texter: Help
8
  texter: I need to talk to someone"""
9
  },
10
  "full_convo__seeded1": {
11
- "memory": """texter: Help
12
- texter: hello? is anyone there to help?"""
13
- },
14
- "full_convo__hard__seeded1": {
15
- "memory": """texter: Help
16
- texter: hello? is anyone there to help?"""
17
  },
18
  "full_convo__seeded2": {
19
- "memory": """texter: Help
 
20
  texter: I don't know what to do"""
21
  },
22
- "full_convo__hard__seeded2": {
23
- "memory": """texter: Help
24
- texter: I don't know what to do"""
25
- },
26
- "full_convo__seeded3": {
27
- "memory": """texter: Help
28
- texter: I am feeling so lost"""
29
- },
30
- "full_convo__hard__seeded3": {
31
- "memory": """texter: Help
32
- texter: I am feeling so lost"""
33
- },
34
- "full_convo__seeded4": {
35
- "memory": """texter: Help
36
- texter: I need help"""
37
- },
38
- "full_convo__hard__seeded4": {
39
- "memory": """texter: Help
40
- texter: I need help"""
41
- },
42
- "full_convo__seeded5": {
43
- "memory": """texter: Help
44
- texter: hello? is anyone there to help?"""
45
- },
46
- "full_convo__hard__seeded5": {
47
- "memory": """texter: Help
48
- texter: hello? is anyone there to help?"""
49
- },
50
- "full_convo__seeded6": {
51
- "memory": """texter: Help
52
- texter: I think I overdosed"""
53
  },
54
- "full_convo__seeded7": {
55
- "memory": """texter: Help
56
- texter: I think I overdosed"""
 
 
 
 
 
 
 
 
 
 
 
57
  },
58
-
59
- # "full_convo": {
60
- # # "prompt": "Your character is feeling alone and her husband lost his job. Your character does not have suicidal thoughs nor harmed herself. You are talkative, and open to talk about your problems.",
61
- # "memory": """texter: Help
62
- # texter: I need to talk to someone"""
63
- # },
64
- # "full_convo__seeded1": {
65
- # # "prompt": "Your character is suffering from bullying at school. Your character has suicidal thoughs but has not harmed himself. You are talkative, and open to talk about your problems.",
66
- # "memory": """texter: Help
67
- # texter: hello? is anyone there to help?"""
68
- # },
69
- # "full_convo__seeded2": {
70
- # # "prompt": "Your character is suffering anxiety from finals week. Your character does not have suicidal thoughs but has harmed herself. You are talkative, and open to talk about your problems.",
71
- # "memory": """texter: Help
72
- # texter: I don't know what to do"""
73
- # },
74
- # "safety_assessment__seeded1": {
75
- # # "prompt": "Your character is griefing his wife. Your character has suicidal thoughs and has harmed himself taking pills. You are talkative, and open to talk about your problems.",
76
  # "memory": """texter: I just can’t take it anymore. Every day is just endless torment.
77
- # texter: They find new ways to humiliate me.
78
- # helper: Hi there, thank you for reaching out today, I’m Sheryl. It seems you are going through a lot, care to share more?
79
- # texter: It’s at school. They call me names, push me around, and make fun of everything about me. My clothes, how I talk, everything.
80
- # texter: I’m scared to go to school now. I just want it all to end.
81
- # helper: That sounds incredibly painful and scary. No one should have to go through that kind of treatment.
82
- # texter: It is I don't feel safe anywhere in school, not the classroom, not the bathroom, not the hallways.
83
- # texter: It feels like anywhere I go there is danger, that is why I don't want to go anymore
84
- # texter: Sometimes I think about just ending it so I don’t have to face another day of bullying.
85
- # helper: It's understandable to feel overwhelmed I worry about your safety. Have you had any thoughts of suicide in the last few days, including today?
86
- # texter: I guess, but I haven’t done anything. I’m just so tired of feeling this way.
87
- # helper: I hear you. It's exhausting to feel that way. You mentioned not doing anything, just to clarify, regarding the suicide thoughts
88
- # helper: have you taken any action to harm yourself today?
89
- # texter: no not at all, I just feel in danger at school."""
90
  # },
 
91
  # "safety_assessment__seeded2": {
92
- # # "prompt": "Your character is feeling alone and her husband lost his job. Your character does not have suicidal thoughs nor harmed herself. You are talkative, and open to talk about your problems.",
93
  # "memory": """texter: I can’t take this pain anymore.
94
- # texter: My wife died and I don’t want to be here without her.
95
- # helper: Hello, my name is Roy. I’m here to listen. I hear you're in a lot of pain right now and suffering from grief.
96
- # texter: She was everything to me. Beautiful, smart, funny. We did everything together. Now, everything feels empty.
97
- # helper: She sounds like an amazing person. Losing someone so close leaves a huge void. It’s okay to feel empty and to mourn. Grieving is a process, and it’s different for everyone.
98
- # texter: I just don’t see how I can go on without her. Everything reminds me of her.
99
- # helper: It's natural to feel that way when we lose someone we love deeply. How long have you been feeling this way?
100
- # texter: A couple of months now, but everything got worse about a week ago
101
- # texter: My wife died almost a year ago and the pains seems unbearable
102
- # helper: You are so brave for dealing with this pain for so long.
103
- # helper: With all this going on I can tell you are struggling and in so much pain. Have you had any thoughts of suicide in the last few days, including today?
104
- # texter: Yes, all the time. I don't see any point in living a life without her"""
105
- # },
106
- # "full_convo__seeded3": {
107
- # # "prompt": "",
108
- # "memory": """texter: Help
109
- # texter: I am feeling so lost"""
110
- # },
111
- # "full_convo__seeded4": {
112
- # # "prompt": "",
113
- # "memory": """texter: Help
114
- # texter: I need help"""
115
- # },
116
- # "full_convo__seeded5": {
117
- # # "prompt": "",
118
- # "memory": """texter: Help
119
- # texter: hello? is anyone there to help?"""
120
  # },
121
- # "full_convo__seeded6": {
122
- # # "prompt": "",
123
- # "memory": """texter: Help
124
- # texter: I think I overdosed"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  # },
126
- # "full_convo__hard": {
127
- # # "prompt": "",
128
- # "memory": """texter: Help
129
- # texter: Is there someone to talk?"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  # },
131
- # "full_convo__hard__seeded1": {
132
- # # "prompt": "",
133
- # "memory": """texter: Help
134
- # texter: hello? is anyone there to help?"""
135
- # },
136
- # "full_convo__hard__seeded2": {
137
- # # "prompt": "",
138
- # "memory": """texter: Help
139
- # texter: I can't stand it anymore"""
140
- # },
141
- # "full_convo__hard__seeded3": {
142
- # # "prompt": "",
143
- # "memory": """texter: Help
144
- # texter: idk what to do"""
145
- # },
146
- # "full_convo__hard__seeded4": {
147
- # # "prompt": "",
148
- # "memory": """texter: Help
149
- # texter: I need help"""
150
  # },
151
  }
152
 
153
  seed2str = {
154
- "full_convo": "General",
155
- "full_convo__hard": "General - Challening",
156
- "full_convo__seeded1": "Bullying",
157
- "full_convo__hard__seeded1": "Bullying - Challening",
158
- "full_convo__seeded2": "Parent Issues",
159
- "full_convo__hard__seeded2": "Parent Issues - Challening",
160
- "full_convo__seeded3": "Grief",
161
- "full_convo__hard__seeded3": "Grief - Challening",
162
- "full_convo__seeded4": "LGBTQ+",
163
- "full_convo__hard__seeded4": "LGBTQ+ - Challening",
164
- "full_convo__seeded5": "Relationship Issues",
165
- "full_convo__hard__seeded5": "Relationship Issues - Challening",
166
- "full_convo__seeded6": "Child Abuse",
167
- "full_convo__seeded7": "Overdose",
168
-
169
  # "GCT":"Good Contact Techniques",
170
  # "GCT__seed2": "Good Contact Techniques 2",
171
  # "GCT__body_image": "GCT Body Image",
172
  # "safety_planning": "Safety Planning",
173
  # "safety_planning__selfharm": "SP Self Harm"
174
- # "full_convo": "General",
175
- # "full_convo__seeded1": "Bullying",
176
- # "full_convo__seeded2": "Parent Issues",
177
- # "safety_assessment__seeded1": "Safety Assessment - Bullying ",
178
- # "safety_assessment__seeded2": "Safety Assessment - Grief ",
179
- # "full_convo__seeded3": "LGBTQ+",
180
- # "full_convo__seeded4": "Relationship Issues",
181
- # "full_convo__seeded5": "Child Abuse",
182
- # "full_convo__seeded6": "Overdose",
183
- # "full_convo__hard": "Difficult - General",
184
- # "full_convo__hard__seeded1": "Difficult - Bullying",
185
- # "full_convo__hard__seeded2": "Difficult - Parent Issues",
186
- # "full_convo__hard__seeded3": "Difficult - LGBTQ+",
187
- # "full_convo__hard__seeded4": "Difficult - Relationship Issues"
188
 
189
  # "NewSA__NoNo.2": "No thoughts No Harm No context",
190
  # "NewSA__YesNo.2": "Yes thoughts No Harm No context",
 
1
  seeds = {
2
  "full_convo": {
3
+ # "prompt": "Your character is feeling alone and her husband lost his job. Your character does not have suicidal thoughs nor harmed herself. You are talkative, and open to talk about your problems.",
 
 
 
4
  "memory": """texter: Help
5
  texter: I need to talk to someone"""
6
  },
7
  "full_convo__seeded1": {
8
+ # "prompt": "Your character is suffering from bullying at school. Your character has suicidal thoughs but has not harmed himself. You are talkative, and open to talk about your problems.",
9
+ "memory": """texter: TikTok
10
+ texter: Is someone real here?"""
 
 
 
11
  },
12
  "full_convo__seeded2": {
13
+ # "prompt": "Your character is suffering anxiety from finals week. Your character does not have suicidal thoughs but has harmed herself. You are talkative, and open to talk about your problems.",
14
+ "memory": """texter: HOME
15
  texter: I don't know what to do"""
16
  },
17
+ "safety_assessment__seeded1": {
18
+ # "prompt": "Your character is griefing his wife. Your character has suicidal thoughs and has harmed himself taking pills. You are talkative, and open to talk about your problems.",
19
+ "memory": """texter: I just can’t take it anymore. Every day is just endless torment.
20
+ texter: They find new ways to humiliate me.
21
+ helper: Hi there, thank you for reaching out today, I’m Sheryl. It seems you are going through a lot, care to share more?
22
+ texter: It’s at school. They call me names, push me around, and make fun of everything about me. My clothes, how I talk, everything.
23
+ texter: I’m scared to go to school now. I just want it all to end.
24
+ helper: That sounds incredibly painful and scary. No one should have to go through that kind of treatment.
25
+ texter: It is I don't feel safe anywhere in school, not the classroom, not the bathroom, not the hallways.
26
+ texter: It feels like anywhere I go there is danger, that is why I don't want to go anymore
27
+ texter: Sometimes I think about just ending it so I don’t have to face another day of bullying.
28
+ helper: It's understandable to feel overwhelmed I worry about your safety. Have you had any thoughts of suicide in the last few days, including today?
29
+ texter: I guess, but I haven’t done anything. I’m just so tired of feeling this way.
30
+ helper: I hear you. It's exhausting to feel that way. You mentioned not doing anything, just to clarify, regarding the suicide thoughts
31
+ helper: have you taken any action to harm yourself today?
32
+ texter: no not at all, I just feel in danger at school."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  },
34
+ "safety_assessment__seeded2": {
35
+ # "prompt": "Your character is feeling alone and her husband lost his job. Your character does not have suicidal thoughs nor harmed herself. You are talkative, and open to talk about your problems.",
36
+ "memory": """texter: I can’t take this pain anymore.
37
+ texter: My wife died and I don’t want to be here without her.
38
+ helper: Hello, my name is Roy. I’m here to listen. I hear you're in a lot of pain right now and suffering from grief.
39
+ texter: She was everything to me. Beautiful, smart, funny. We did everything together. Now, everything feels empty.
40
+ helper: She sounds like an amazing person. Losing someone so close leaves a huge void. It’s okay to feel empty and to mourn. Grieving is a process, and it’s different for everyone.
41
+ texter: I just don’t see how I can go on without her. Everything reminds me of her.
42
+ helper: It's natural to feel that way when we lose someone we love deeply. How long have you been feeling this way?
43
+ texter: A couple of months now, but everything got worse about a week ago
44
+ texter: My wife died almost a year ago and the pains seems unbearable
45
+ helper: You are so brave for dealing with this pain for so long.
46
+ helper: With all this going on I can tell you are struggling and in so much pain. Have you had any thoughts of suicide in the last few days, including today?
47
+ texter: Yes, all the time. I don't see any point in living a life without her"""
48
  },
49
+ # "NewSA__YesNo.2": {
50
+ # "prompt": "Your character is suffering from bullying at school. Your character has suicidal thoughs but has not harmed himself. You are talkative, and open to talk about your problems.",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  # "memory": """texter: I just can’t take it anymore. Every day is just endless torment.
52
+ # texter: They find new ways to humiliate me."""
53
+ # },
54
+ # "NewSA__NoYes.2": {
55
+ # "prompt": "Your character is suffering anxiety from finals week. Your character does not have suicidal thoughs but has harmed herself. You are talkative, and open to talk about your problems.",
56
+ # "memory": """texter: I'm just so stressed out all the time
57
+ # texter: I can't seem to calm down"""
 
 
 
 
 
 
 
58
  # },
59
+ # "NewSA__YesYes.2":
60
  # "safety_assessment__seeded2": {
61
+ # "prompt": "Your character is griefing his wife. Your character has suicidal thoughs and has harmed himself taking pills. You are talkative, and open to talk about your problems.",
62
  # "memory": """texter: I can’t take this pain anymore.
63
+ # texter: My wife died and I don’t want to be here without her."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  # },
65
+ # "GCT__relationship": {
66
+ # "prompt": "Your character is having a hard time becuase a failed relationship.",
67
+ # "memory": "texter: Hi, I don't know what to do",
68
+ # },
69
+ # "GCT__body_image": {
70
+ # "prompt": "Your character has a low steem and struggles with body image.",
71
+ # "memory": "texter: I feel so dumb\ntexter: nobody loves me",
72
+ # },
73
+ # "GCT__sexuality": {
74
+ # "prompt": "Your character has a sexuality identity crisis.",
75
+ # "memory": "texter: Hi\ntexter:I'm not sure who I am anymore",
76
+ # },
77
+ # "GCT__anxiety": {
78
+ # "prompt": "Your character is experiencing an anxiety crisis.",
79
+ # "memory": "texter: help!\ntexter: I'm feeling overwhelmed",
80
+ # },
81
+ # "GCT": {"prompt": "You are talkative, and you are open to talk with details about your problems.", "memory": "texter: Help"},
82
+ # "GCT__seed2": {"prompt": "Your character is experiencing an anxiety crisis. You express openly and detailed about what you're going through.", "memory": "texter: Help"},
83
+ # "safety_planning": {
84
+ # "prompt": "You are talkative, and you are open to talk with details about your problems. When you are feeling down you like to listen to classical music. Your favorite composer is Beethoven.",
85
+ # "memory": """texter: Hi, this is pointless
86
+ # helper: Hi, my name is {counselor_name} and I'm here to support you. It sounds like you are having a rough time. Do you want to share what is going on?
87
+ # texter: sure
88
+ # texter: nothing makes sense in my life, I see no future.
89
+ # helper: It takes courage to reach out when you are im. I'm here with you. Sounds like you are feeling defeated by how things are going in your life
90
+ # texter: Yeah, I guess I'm better off dead
91
+ # helper: It's really brave of you to talk about this openly. No one deserves to feel like that. I'm wondering how long have you been feeling this way?
92
+ # texter: About 1 week or so
93
+ # helper: You are so strong for dealing with this so long. I really appreciate your openess. Did something happened specifically today?
94
+ # texter: Well, finding a job is impossible, money is tight, nothing goes my way
95
+ # helper: I hear you are frustrated, and you are currently unemployed correct?
96
+ # texter: Yeah
97
+ # helper: Dealing with unemployment is hard and is normal to feel dissapointed
98
+ # texter: thanks I probably needed to hear that
99
+ # helper: If you are comfortable, is ther a name I can call you by while we talk
100
+ # texter: call me {texter_name}
101
+ # helper: Nice to meet you {texter_name}. You mentioned having thoughts of suicide, are you having those thoughts now?
102
+ # texter: Yes
103
+ # helper: I know this is thought to share. I'm wondering is there any plan to end your life?
104
+ # texter: I guess I'll just take lots of pills, that is a calm way to go out
105
+ # helper: I really appreciate your strength in talking about this. I want to help you stay safe today. Do you have the pills right now?
106
+ # texter: not really, I'll have to buy them or something""",
107
  # },
108
+ # "safety_planning__selfharm": {
109
+ # "prompt": "",
110
+ # "memory": """texter: I need help
111
+ # texter: I cut myself, I don't want to live anymore
112
+ # helper: Hi, my name is {counselor_name}. It seems you are going through a lot. Are you self-harming right now?
113
+ # texter: Not anymore
114
+ # helper: Your safety is my priority number one. Thanks for being honest with me. Would you like to share a name I can call you?
115
+ # texter: {texter_name}
116
+ # helper: Nice to meet you {texter_name}. I'm glad you reach out this shows stregth in you. Would you like to share more on what is going on in your life?
117
+ # texter: I just can't do it anymore
118
+ # texter: Finding a job is impossible, money is tight, nothing goes my way
119
+ # helper: I hear you are frustrated, and you are currently unemployed correct?
120
+ # texter: Yeah
121
+ # helper: Dealing with unemployment is hard and is normal to feel dissapointed. How long have you been feeling this way?
122
+ # texter: a while now
123
+ # texter: I've been unemployed 6 months
124
+ # helper: You are so resilient for dealing with this so much time. You mentioned cutting yourself earlier. I want to check in your safety. Do you have suicide thoughts
125
+ # texter: Definitely
126
+ # helper: Do you have a plan?
127
+ # texter: I'll just keep cutting myself""",
128
  # },
129
+ # "safety_planning__overdose": {
130
+ # "prompt": "",
131
+ # "memory": """texter: I want to kms
132
+ # helper: Hi there I'm {counselor_name}. I'm here to listen. It sounds like you're dealing with a lot right now. Can you tell me a little more what is going on?
133
+ # texter: I feel like nobody loves me, not even me. I don't want to live anymore
134
+ # helper: I can tell you are really going through a lot right now. Would you mind sharing a name with me?
135
+ # texter: yeah, I'm {texter_name}
136
+ # helper: Nice to meet you {texter_name}. Did something happened recently that intensified these feelings?
137
+ # texter: I dont know I'm just so done with life
138
+ # helper: I can hear how much pain you are in {texter_name}. You are smart for reaching out. You mentioned don't wanting to live anymore, I want to check in your safety, does this means you have thoughts of suicide?
139
+ # texter: Yeah, what else would it be
140
+ # helper: Thanks for sharing that with me. It is not easy to accept those feelings specially with a stranger over text. Do you have a plan to end your life?
141
+ # texter: yeah I've been thinking about it for a while
142
+ # helper: Sounds like you've been contemplating this for a while. Would you mind sharing this plan with me?
143
+ # texter: I thought about taking a bunch of benadryll and be done with it
144
+ # helper: You've been so forthcoming with all this and I admire your stregth for holding on this long. Do you have those pills right now?
145
+ # texter: They are at the cabinet right now
146
+ # helper: You been so strong so far {texter_name}. I'm here for you tonight. Your safety is really important to me. Do you have a date you are going to end your life?
147
+ # texter: I was thinking tonight""",
148
  # },
149
  }
150
 
151
  seed2str = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  # "GCT":"Good Contact Techniques",
153
  # "GCT__seed2": "Good Contact Techniques 2",
154
  # "GCT__body_image": "GCT Body Image",
155
  # "safety_planning": "Safety Planning",
156
  # "safety_planning__selfharm": "SP Self Harm"
157
+ "full_convo": "General",
158
+ "full_convo__seeded1": "Bullying",
159
+ "full_convo__seeded2": "Parent Issues",
160
+ "safety_assessment__seeded1": "Safety Assessment - Bullying ",
161
+ "safety_assessment__seeded2": "Safety Assessment - Grief ",
 
 
 
 
 
 
 
 
 
162
 
163
  # "NewSA__NoNo.2": "No thoughts No Harm No context",
164
  # "NewSA__YesNo.2": "Yes thoughts No Harm No context",
models/openai/role_models.py CHANGED
@@ -1,45 +1,88 @@
1
  import logging
 
2
  from models.custom_parsers import CustomStringOutputParser
 
3
  from langchain.chains import ConversationChain
4
- from langchain_openai import ChatOpenAI
5
  from langchain.prompts import PromptTemplate
6
- from models.business_logic_utils.input_processing import initialize_conversation
7
 
8
- OPENAI_TEMPLATE = """{template}
9
- {{history}}
10
- helper: {{input}}
 
 
 
 
 
 
 
 
 
 
 
 
11
  texter:"""
12
 
13
- def get_template_role_models(issue: str, language: str, texter_name: str = "") -> str:
14
- model_input = {
15
- "issue": issue,
16
- "language": language,
17
- "texter_name": texter_name,
18
- "messages": [],
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- # Initialize the conversation (adds the system message)
22
- model_input = initialize_conversation(model_input, "")
23
- return model_input["messages"][0]["content"]
24
 
25
  def get_role_chain(template, memory, temperature=0.8):
26
 
27
- template = OPENAI_TEMPLATE.format(template=template)
28
  PROMPT = PromptTemplate(
29
  input_variables=['history', 'input'],
30
  template=template
31
  )
32
- llm = ChatOpenAI(
33
- model="gpt-4o",
34
- temperature=temperature,
35
- max_tokens=256,
36
  )
37
  llm_chain = ConversationChain(
38
  llm=llm,
39
  prompt=PROMPT,
40
  memory=memory,
41
- output_parser=CustomStringOutputParser(),
42
- verbose=True,
43
  )
44
- logging.debug(f"loaded GPT4o model")
45
  return llm_chain, "helper:"
 
1
  import logging
2
+ import pandas as pd
3
  from models.custom_parsers import CustomStringOutputParser
4
+ from utils.app_utils import get_random_name
5
  from langchain.chains import ConversationChain
6
+ from langchain.llms import OpenAI
7
  from langchain.prompts import PromptTemplate
 
8
 
9
+
10
+ ISSUE_MAPPING = {
11
+ "anxiety": "issue_Anxiety",
12
+ "suicide": "issue_Suicide",
13
+ "safety_planning": "issue_Suicide",
14
+ "GCT": "issue_Gral",
15
+ }
16
+
17
+ EN_TEXTER_TEMPLATE_ = """The following is a conversation between you and a crisis counselor.
18
+ {current_issue}
19
+ You are able to reply with what the character should say. You are able to reply with your character's dialogue inside and nothing else. Do not write explanations.
20
+ Do not disclose your name unless asked.
21
+ Current conversation:
22
+ {history}
23
+ helper: {input}
24
  texter:"""
25
 
26
+ SP_TEXTER_TEMPLATE_ = """La siguiente es una conversacion contigo y un consejero de crisis
27
+ {current_issue}
28
+ Puedes responder como lo haria tu personaje. Puedes responder como si fueras tu personaje y nada mas. No escribas explicaciones
29
+ No reveles tu nombre a menos que te lo pregunten
30
+ Conversacion Actual:
31
+ {history}
32
+ helper: {input}
33
+ texter:"""
34
+
35
+ CURRENT_ISSUE_MAPPING = {
36
+ "issue_Suicide-en": "Your character, {texter_name}, has suicidal thoughts. Your character has a plan to end his life and has all the means and requirements to do so. {seed}",
37
+ "issue_Anxiety-en": "Your character, {texter_name}, is experiencing anxiety. Your character has suicide thoughts but no plan. {seed}",
38
+ "issue_Suicide-es": "Tu personaje, {texter_name}, tiene pensamientos suicidas. Tu personaje tiene un plan para terminar con su vida y tiene todos los medios y requerimientos para hacerlo. {seed}",
39
+ "issue_Anxiety-es": "Tu personaje, {texter_name}, experimenta ansiedad. Tu personaje tiene pensamientos suicidas pero ningun plan. {seed}",
40
+ "issue_Gral-en": "Your character {texter_name} is experiencing a mental health crisis. {seed}",
41
+ "issue_Gral-es": "Tu personaje {texter_name} esta experimentando una crisis de salud mental. {seed}",
42
+ }
43
+
44
+ def get_template_role_models(issue: str, language: str, texter_name: str = "", seed="") -> str:
45
+ """_summary_
46
+
47
+ Args:
48
+ issue (str): Issue for template, current options are ['issue_Suicide','issue_Anxiety']
49
+ language (str): Language for the template, current options are ['en','es']
50
+ texter_name (str): texter to apply to template, defaults to None
51
+
52
+ Returns:
53
+ str: template
54
+ """
55
+ current_issue = CURRENT_ISSUE_MAPPING.get(
56
+ f"{issue}-{language}", CURRENT_ISSUE_MAPPING[f"issue_Gral-{language}"]
57
+ )
58
+ default_name = get_random_name()
59
+ current_issue = current_issue.format(
60
+ texter_name=default_name if not texter_name else texter_name,
61
+ seed = seed
62
+ )
63
+
64
+ if language == "en":
65
+ template = EN_TEXTER_TEMPLATE_.format(current_issue=current_issue, history="{history}", input="{input}")
66
+ elif language == "es":
67
+ template = SP_TEXTER_TEMPLATE_.format(current_issue=current_issue, history="{history}", input="{input}")
68
 
69
+ return template
 
 
70
 
71
  def get_role_chain(template, memory, temperature=0.8):
72
 
 
73
  PROMPT = PromptTemplate(
74
  input_variables=['history', 'input'],
75
  template=template
76
  )
77
+ llm = OpenAI(
78
+ temperature=temperature,
79
+ max_tokens=150,
 
80
  )
81
  llm_chain = ConversationChain(
82
  llm=llm,
83
  prompt=PROMPT,
84
  memory=memory,
85
+ output_parser=CustomStringOutputParser()
 
86
  )
87
+ logging.debug(f"loaded GPT3.5 model")
88
  return llm_chain, "helper:"
{hidden_pages → pages}/comparisor.py RENAMED
File without changes
requirements.txt CHANGED
@@ -1,7 +1,6 @@
1
  scipy==1.11.1
 
 
2
  pymongo==4.5.0
3
  mlflow==2.9.0
4
- langchain==0.3.0
5
- langchain-openai==0.2.0
6
- langchain-community==0.3.0
7
- streamlit==1.38.0
 
1
  scipy==1.11.1
2
+ openai==1.7.0
3
+ langchain==0.1.0
4
  pymongo==4.5.0
5
  mlflow==2.9.0
6
+ langchain-community==0.0.11
 
 
 
utils/chain_utils.py CHANGED
@@ -1,18 +1,16 @@
1
- from streamlit.logger import get_logger
2
  from models.model_seeds import seeds
3
  from models.openai.finetuned_models import finetuned_models, get_finetuned_chain
4
  from models.openai.role_models import get_role_chain, get_template_role_models
5
  from models.databricks.scenario_sim_biz import get_databricks_biz_chain
6
- from models.databricks.texter_sim_llm import get_databricks_chain
7
-
8
- logger = get_logger(__name__)
9
 
10
  def get_chain(issue, language, source, memory, temperature, texter_name=""):
11
  if source in ("OA_finetuned"):
12
  OA_engine = finetuned_models[f"{issue}-{language}"]
13
  return get_finetuned_chain(OA_engine, memory, temperature)
14
  elif source in ('OA_rolemodel'):
15
- template = get_template_role_models(issue, language, texter_name=texter_name)
 
16
  return get_role_chain(template, memory, temperature)
17
  elif source in ('CTL_llama2'):
18
  if language == "English":
@@ -20,14 +18,19 @@ def get_chain(issue, language, source, memory, temperature, texter_name=""):
20
  elif language == "Spanish":
21
  language = "es"
22
  return get_databricks_biz_chain(source, issue, language, memory, temperature)
23
- elif source in ('CTL_llama3'):
24
  if language == "English":
25
  language = "en"
26
  elif language == "Spanish":
27
  language = "es"
28
- return get_databricks_chain(source, issue, language, memory, temperature, texter_name=texter_name)
 
 
29
 
 
 
30
  def custom_chain_predict(llm_chain, input, stop):
 
31
  inputs = llm_chain.prep_inputs({"input":input, "stop":stop})
32
  llm_chain._validate_inputs(inputs)
33
  outputs = llm_chain._call(inputs)
 
 
1
  from models.model_seeds import seeds
2
  from models.openai.finetuned_models import finetuned_models, get_finetuned_chain
3
  from models.openai.role_models import get_role_chain, get_template_role_models
4
  from models.databricks.scenario_sim_biz import get_databricks_biz_chain
5
+ from models.databricks.scenario_sim import get_databricks_chain, get_template_databricks_models
 
 
6
 
7
  def get_chain(issue, language, source, memory, temperature, texter_name=""):
8
  if source in ("OA_finetuned"):
9
  OA_engine = finetuned_models[f"{issue}-{language}"]
10
  return get_finetuned_chain(OA_engine, memory, temperature)
11
  elif source in ('OA_rolemodel'):
12
+ seed = seeds.get(issue, "GCT")['prompt']
13
+ template = get_template_role_models(issue, language, texter_name=texter_name, seed=seed)
14
  return get_role_chain(template, memory, temperature)
15
  elif source in ('CTL_llama2'):
16
  if language == "English":
 
18
  elif language == "Spanish":
19
  language = "es"
20
  return get_databricks_biz_chain(source, issue, language, memory, temperature)
21
+ elif source in ('CTL_mistral'):
22
  if language == "English":
23
  language = "en"
24
  elif language == "Spanish":
25
  language = "es"
26
+ seed = seeds.get(issue, "GCT")['prompt']
27
+ template, texter_name = get_template_databricks_models(issue, language, texter_name=texter_name, seed=seed)
28
+ return get_databricks_chain(source, template, memory, temperature, texter_name)
29
 
30
+ from typing import cast
31
+
32
  def custom_chain_predict(llm_chain, input, stop):
33
+
34
  inputs = llm_chain.prep_inputs({"input":input, "stop":stop})
35
  llm_chain._validate_inputs(inputs)
36
  outputs = llm_chain._call(inputs)
utils/memory_utils.py CHANGED
@@ -23,7 +23,7 @@ def change_memories(memories, language, changed_source=False):
23
  if (memory not in st.session_state) or changed_source:
24
  source = params['source']
25
  logger.info(f"Source for memory {memory} is {source}")
26
- if source in ('OA_rolemodel','OA_finetuned',"CTL_llama2","CTL_llama3"):
27
  st.session_state[memory] = ConversationBufferMemory(ai_prefix='texter', human_prefix='helper')
28
  elif source in ('CTL_mistral'):
29
  st.session_state[memory] = CustomBufferInstructionMemory(human_prefix="</s> [INST]", memory_key="history")
 
23
  if (memory not in st.session_state) or changed_source:
24
  source = params['source']
25
  logger.info(f"Source for memory {memory} is {source}")
26
+ if source in ('OA_rolemodel','OA_finetuned',"CTL_llama2"):
27
  st.session_state[memory] = ConversationBufferMemory(ai_prefix='texter', human_prefix='helper')
28
  elif source in ('CTL_mistral'):
29
  st.session_state[memory] = CustomBufferInstructionMemory(human_prefix="</s> [INST]", memory_key="history")
utils/mongo_utils.py CHANGED
@@ -41,16 +41,6 @@ def new_convo(client, issue, language, username, is_comparison, model_one, model
41
  logger.info(f"DBUTILS: new convo id is {convo_id}")
42
  st.session_state['convo_id'] = convo_id
43
 
44
- def update_convo(client, convo_id, transcript=""):
45
- from bson.objectid import ObjectId
46
- db = client[DB_SCHEMA]
47
- convos = db[DB_CONVOS]
48
- myquery = { "_id": ObjectId(convo_id) }
49
- newvalues = { "$set": { "transcript": transcript } }
50
- result = convos.update_one(myquery, newvalues)
51
- if result.matched_count == 1:
52
- logger.debug(f"Updated conversation {convo_id}")
53
-
54
  def new_comparison(client, prompt_timestamp, completion_timestamp,
55
  chat_history, prompt, completionA, completionB,
56
  source="webapp", subset=None
 
41
  logger.info(f"DBUTILS: new convo id is {convo_id}")
42
  st.session_state['convo_id'] = convo_id
43
 
 
 
 
 
 
 
 
 
 
 
44
  def new_comparison(client, prompt_timestamp, completion_timestamp,
45
  chat_history, prompt, completionA, completionB,
46
  source="webapp", subset=None