Spaces:
Running
Running
new_v2_llama3
#7
by
ivnban27-ctl
- opened
- app_config.py +5 -11
- convosim.py +12 -26
- models/business_logic_utils/business_logic.py +0 -39
- models/business_logic_utils/config.py +0 -374
- models/business_logic_utils/input_processing.py +0 -143
- models/business_logic_utils/prompt_generation.py +0 -101
- models/business_logic_utils/requirements.txt +0 -3
- models/business_logic_utils/response_generation.py +0 -49
- models/business_logic_utils/response_processing.py +0 -197
- models/custom_parsers.py +30 -30
- models/databricks/custom_databricks_llm.py +0 -72
- models/databricks/scenario_sim.py +91 -0
- models/databricks/texter_sim_llm.py +0 -46
- models/model_seeds.py +133 -159
- models/openai/role_models.py +66 -23
- {hidden_pages → pages}/comparisor.py +0 -0
- requirements.txt +3 -4
- utils/chain_utils.py +10 -7
- utils/memory_utils.py +1 -1
- utils/mongo_utils.py +0 -10
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 |
-
|
7 |
-
"CTL_llama3",
|
8 |
# "CTL_mistral",
|
9 |
'OA_rolemodel',
|
10 |
# 'OA_finetuned',
|
11 |
]
|
12 |
-
SOURCES_LAB = {"OA_rolemodel":'OpenAI
|
13 |
"OA_finetuned":'Finetuned OpenAI',
|
14 |
-
|
15 |
-
"CTL_llama3": "Llama 3",
|
16 |
"CTL_mistral": "Mistral",
|
17 |
}
|
18 |
|
19 |
ENDPOINT_NAMES = {
|
20 |
-
|
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
|
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,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.
|
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='
|
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 |
-
|
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():
|
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 |
-
|
24 |
-
|
25 |
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
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 |
-
"
|
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 |
-
"
|
12 |
-
|
13 |
-
|
14 |
-
"full_convo__hard__seeded1": {
|
15 |
-
"memory": """texter: Help
|
16 |
-
texter: hello? is anyone there to help?"""
|
17 |
},
|
18 |
"full_convo__seeded2": {
|
19 |
-
"
|
|
|
20 |
texter: I don't know what to do"""
|
21 |
},
|
22 |
-
"
|
23 |
-
"
|
24 |
-
texter: I
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
texter: I
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
texter: I
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
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 |
-
"
|
55 |
-
"
|
56 |
-
texter: I
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
},
|
58 |
-
|
59 |
-
#
|
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 |
-
#
|
79 |
-
#
|
80 |
-
#
|
81 |
-
#
|
82 |
-
# texter:
|
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 |
-
#
|
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 |
-
#
|
122 |
-
#
|
123 |
-
#
|
124 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
# },
|
126 |
-
# "
|
127 |
-
#
|
128 |
-
# "memory": """texter:
|
129 |
-
# texter:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
# },
|
131 |
-
# "
|
132 |
-
#
|
133 |
-
# "memory": """texter:
|
134 |
-
#
|
135 |
-
#
|
136 |
-
#
|
137 |
-
#
|
138 |
-
#
|
139 |
-
# texter: I
|
140 |
-
#
|
141 |
-
#
|
142 |
-
#
|
143 |
-
#
|
144 |
-
#
|
145 |
-
#
|
146 |
-
#
|
147 |
-
#
|
148 |
-
#
|
149 |
-
# texter: I
|
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 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
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
|
5 |
from langchain.prompts import PromptTemplate
|
6 |
-
from models.business_logic_utils.input_processing import initialize_conversation
|
7 |
|
8 |
-
|
9 |
-
{
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
texter:"""
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
|
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 =
|
33 |
-
|
34 |
-
|
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
|
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.
|
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.
|
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 |
-
|
|
|
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 ('
|
24 |
if language == "English":
|
25 |
language = "en"
|
26 |
elif language == "Spanish":
|
27 |
language = "es"
|
28 |
-
|
|
|
|
|
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"
|
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
|