Links to feedback form

#3
README.md CHANGED
@@ -6,6 +6,6 @@ colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
- short_description: A space for hosting our experiment website tehe
10
  ---
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
+ short_description: A space for hosting our experiment website
10
  ---
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
chat_application/main.py CHANGED
@@ -1,4 +1,3 @@
1
- from google.api_core import exceptions
2
  from flask import Flask, request, render_template, redirect, url_for, session, make_response, render_template_string
3
  from flask_socketio import SocketIO, join_room, leave_room, send
4
  from pymongo import MongoClient
@@ -29,17 +28,14 @@ from duplicate_detection import duplicate_check
29
  from huggingface_hub import upload_folder
30
  from huggingface_hub import HfApi
31
  from huggingface_hub import login
32
- from datetime import datetime
33
-
34
 
35
  class datasetHandler():
36
 
37
- def __init__(self,hf_token,mongoDB_name,max_dumps = 10):
38
  login(hf_token)
39
  self.api = HfApi(token = hf_token)
40
  self.DB_name = mongoDB_name
41
- self.timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
42
- self.max_dumps = max_dumps
43
 
44
  def make_dump(self):
45
  try:
@@ -51,47 +47,21 @@ class datasetHandler():
51
 
52
  def upload_dump(self):
53
  try:
54
- upload_folder(folder_path="/tmp/mongoDBContents",path_in_repo=f"mongoDump_{self.timestamp}", repo_id="ProjectFrozone/MongoDBDumps", repo_type="dataset")
55
  return 0
56
  except Exception as e:
57
  print(e)
58
  return 1
59
 
60
- def list_dumps(self):
61
- all_files = self.api.list_repo_files(repo_id="ProjectFrozone/MongoDBDumps", repo_type="dataset")
62
- all_dirs = [f[:f.index("/")] for f in all_files if "mongoDump_" in f]
63
- dates = [date[date.index("_") + 1:] for date in all_dirs]
64
- return (all_dirs, dates)
65
-
66
- def delete_dump(self,dump_name):
67
- self.api.delete_folder(
68
- repo_id="ProjectFrozone/MongoDBDumps",
69
- path_in_repo=f"{dump_name}",
70
- repo_type="dataset",
71
- commit_message=f"Deleted {dump_name}"
72
- )
73
-
74
- def cleanup_dataset(self,dirs,dates):
75
- if len(dates) > self.max_dumps:
76
- to_remove = dirs[0]
77
- self.delete_dump(to_remove)
78
- return f"Deleted {to_remove}"
79
- return "Nothing to delete"
80
-
81
  def dump_db(self):
82
  self.make_dump()
83
  self.upload_dump()
84
-
85
- def clean(self):
86
- dirs,dates = self.list_dumps()
87
- print(self.cleanup_dataset(dirs,dates))
88
- # End database backup code
89
 
90
  #controls
91
  CHAT_CONTEXT = 20 #how many messages from chat history to append to inference prompt
92
  #minimum number of chars where we start checking for duplicate messages
93
  DUP_LEN = 25 #since short messages may reasonably be the same
94
- REMOVE_PUNC_RATE = .8 #how often to remove final punctuation
95
 
96
  # Directory alignment
97
  BASE_DIR = Path(__file__).resolve().parent
@@ -137,15 +107,12 @@ frobot = GenerativeModel(frotj.tuned_model_endpoint_name)
137
  #change to endpoints
138
  hotbot = "projects/700531062565/locations/us-central1/endpoints/6225523347153747968"
139
  coolbot = "projects/700531062565/locations/us-central1/endpoints/1700531621553242112"
140
- #frobot = "projects/700531062565/locations/us-central1/endpoints/2951406418055397376"
141
- #make frobot actually a coolbot (!4/30)
142
- frobot = coolbot
143
 
144
  # MongoDB setup
145
  client = MongoClient("mongodb://127.0.0.1:27017/")
146
  db = client["huggingFaceData"]
147
  rooms_collection = db.rooms
148
- feedback_collection = db.feedback
149
 
150
  # List of fruits to choose display names from
151
  FRUIT_NAMES = ["blueberry", "strawberry", "orange", "cherry"]
@@ -180,16 +147,12 @@ TOPICS_LIST = [
180
  }
181
  ]
182
 
183
- """
184
- (!4/30) Make frobot actually a coolbot
185
-
186
  # FroBot Main Prompt
187
  with open(PROJECT_ROOT / "data" / "prompts" / "frobot_prompt_main.txt") as f:
188
  FROBOT_PROMPT = f.read()
189
  # Instructions
190
  with open(PROJECT_ROOT / "data" / "inference_instructions" / "frobot_instructions_main.txt") as f:
191
  FROBOT_INSTRUCT = f.read()
192
- """
193
 
194
  # HotBot Prompt
195
  with open(PROJECT_ROOT / "data" / "prompts" / "hotbot_prompt_main.txt") as h:
@@ -205,10 +168,6 @@ with open(PROJECT_ROOT / "data" / "prompts" / "coolbot_prompt_main.txt") as c:
205
  with open(PROJECT_ROOT / "data" / "inference_instructions" / "coolbot_instructions_main.txt") as c:
206
  COOLBOT_INSTRUCT = c.read()
207
 
208
- # (!4/30) point frobot instructions at coolbot's
209
- FROBOT_PROMPT = COOLBOT_PROMPT
210
- FROBOT_INSTRUCT = COOLBOT_INSTRUCT
211
-
212
  # Randomly select fruits to use for display names
213
  def choose_names(n):
214
  # Return n unique random fruit names
@@ -312,12 +271,6 @@ def replace_semicolons(text, probability=0.80):
312
  modified_text.append(char)
313
  return ''.join(modified_text)
314
 
315
- def get_last_paragraph(text):
316
- text = text.strip()
317
- if "\n" not in text:
318
- return text
319
- return text.rsplit("\n", 1)[-1].strip()
320
-
321
  def get_response_delay(response):
322
  baseDelay = 5 # standard delay for thinking
323
  randFactor = np.random.uniform(0,30, len(response) // 4)
@@ -331,7 +284,7 @@ def get_response_delay(response):
331
 
332
  # Ask a bot for its response, store in DB, and send to client
333
  # Returns true if the bot passed
334
- def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wait_time = 1):
335
  # Prevents crashing if bot model did not load
336
  if bot is None:
337
  return False
@@ -364,35 +317,6 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
364
  ),
365
  )
366
  parsed_response = response.candidates[0].content.parts[0].text.strip()
367
-
368
- # Deal with rate limit issues
369
- except exceptions.TooManyRequests:
370
- print(f"429 Rate Limit Exceeded")
371
- socketio.sleep(wait_time)
372
- wait_time *= 2
373
-
374
- # Prevent Stack Overflow
375
- if wait_time > 32:
376
- print("Rate Limit Exceeded and Exponential Backoff Too Long")
377
- print("Treating this bot's response as a pass.")
378
- room_doc = rooms_collection.find_one({"_id": room_id})
379
- if not room_doc or room_doc.get("ended", False):
380
- return False
381
- # Store the error response in the database
382
- bot_message = {
383
- "sender": bot_display_name,
384
- "message": "ERROR in bot response - treated as a (pass)",
385
- "timestamp": datetime.utcnow()
386
- }
387
- rooms_collection.update_one(
388
- {"_id": room_id},
389
- {"$push": {"messages": bot_message}}
390
- )
391
- return True
392
-
393
- return ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wait_time = wait_time)
394
-
395
-
396
  except Exception as e:
397
  print("Error in bot response: ", e)
398
  print("Treating this bot's response as a pass.")
@@ -423,19 +347,6 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
423
  parsed_response = re.sub(r"\b"
424
  + aliases[bot_display_name]
425
  + r"\b:\s?", '', parsed_response)
426
-
427
- # Only keep the last paragraph of frobot responses
428
- """
429
- (!4/30) commenting this cout because the CCHU experiment does not have a frobot
430
- We do not want this happening for frobot = coolbot
431
-
432
- if bot == frobot:
433
- print("=========== OG FROBOT RESPONSE")
434
- print(parsed_response)
435
- parsed_response = get_last_paragraph(parsed_response)
436
- print("=========== LAST PARAGRAPH")
437
- print(parsed_response)
438
- """
439
 
440
  # Check for if the bot passed (i.e. response = "(pass)")
441
  if ("(pass)" in parsed_response) or (parsed_response == ""):
@@ -457,26 +368,21 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
457
  print("PASSED")
458
  return True # a pass is still recorded in the database, but not sent to the client
459
 
460
- #sub letters for names, so if the bot addressed A -> Apple
461
- named_response = let_to_name(room_id, parsed_response)
462
  #remove encapsulating quotes
463
- no_quotes = remove_quotes(named_response)
464
  #humanize the response (remove obvious AI formatting styles)
465
  humanized_response = humanize(no_quotes)
466
  #replace most semicolons
467
  less_semicolons_response = replace_semicolons(humanized_response)
468
  #corrupt the response (add some typos and misspellings)
469
- corrupted_response = corrupt(less_semicolons_response, misspell_aug_p=0.01, typo_aug_p=0.005)
470
  #remove weird chars
471
  no_weird_chars = remove_weird_characters(corrupted_response)
472
- #remove trailing punctuation % of the time
473
- if random.random() < REMOVE_PUNC_RATE:
474
- no_weird_chars = re.sub(r'[^\w\s]+$', '', no_weird_chars)
475
-
476
- final_response = no_weird_chars
477
 
478
  #check that there are no reccent duplicate messages
479
- if len(final_response) > DUP_LEN and duplicate_check(final_response, context):
480
  print("****DUPLICATE MESSAGE DETECTED")
481
  print("Treating this bot's response as a pass.")
482
  # Do not store/send messages if the chat has ended
@@ -486,7 +392,7 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
486
  # Store the error response in the database
487
  bot_message = {
488
  "sender": bot_display_name,
489
- "message": f"DUPLICATE message detected - treated as a (pass) : {final_response}",
490
  "timestamp": datetime.utcnow()
491
  }
492
  rooms_collection.update_one(
@@ -501,7 +407,7 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
501
  print(corrupted_response)
502
 
503
  # Add latency/wait time for bot responses
504
- delay = get_response_delay(final_response);
505
  print(delay)
506
  time.sleep(delay)
507
 
@@ -513,7 +419,7 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
513
  # Store the response in the database
514
  bot_message = {
515
  "sender": bot_display_name,
516
- "message": final_response, #save fruits in db so page reload shows proper names
517
  "timestamp": datetime.utcnow()
518
  }
519
  rooms_collection.update_one(
@@ -522,7 +428,7 @@ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt , wa
522
  )
523
 
524
  # Send the bot's response to the client
525
- socketio.emit("message", {"sender": bot_display_name, "message": final_response}, to=room_id)
526
  return False
527
 
528
  def ask_bot_round(room_id):
@@ -554,7 +460,6 @@ def backup_mongo(time):
554
  token = os.getenv("HF_TOKEN")
555
  handler = datasetHandler(token , 'huggingFaceData')
556
  handler.dump_db()
557
- handler.clean()
558
  socketio.sleep(time)
559
 
560
  # Build the routes
@@ -582,8 +487,7 @@ def home():
582
  session['user_id'] = user_id
583
  return redirect(url_for('topics'))
584
  else:
585
- link = f"https://umw.qualtrics.com/jfe/form/SV_08v26NssCOwZTP8?PROLIFIC_PID={prolific_pid}"
586
- return render_template('home.html',prolific_pid=prolific_pid, feedback_form_url=link)
587
 
588
  @app.route('/topics', methods=["GET", "POST"])
589
  def topics():
@@ -655,14 +559,6 @@ def choose():
655
  "ended": False,
656
  "ended_at": None
657
  })
658
- # Create the new feedback in the database
659
- feedback_collection.insert_one({
660
- "_id": room_id,
661
- # creation date/time
662
- "created_at": datetime.utcnow(),
663
- # user identity
664
- "user_id": user_id,
665
- })
666
 
667
  session['room'] = room_id
668
  session['display_name'] = user_name
@@ -685,18 +581,8 @@ def room():
685
  m for m in room_doc["messages"]
686
  if len(re.findall(r"pass",m.get("message", "").strip())) == 0
687
  ]
688
- if session.get('user_id'):
689
- link = f"https://umw.qualtrics.com/jfe/form/SV_08v26NssCOwZTP8?PROLIFIC_PID={session.get('user_id')}"
690
- return render_template("room.html", room=room_id, topic_info=topic_info, user=display_name,
691
- messages=nonpass_messages, FroBot_name=room_doc["FroBot_name"],
692
- HotBot_name=room_doc["HotBot_name"], CoolBot_name=room_doc["CoolBot_name"],
693
- ended=room_doc["ended"], feedback_form_url=link)
694
- else:
695
- return render_template("room.html", room=room_id, topic_info=topic_info, user=display_name,
696
- messages=nonpass_messages, FroBot_name=room_doc["FroBot_name"],
697
- HotBot_name=room_doc["HotBot_name"], CoolBot_name=room_doc["CoolBot_name"],
698
- ended=room_doc["ended"])
699
-
700
  @app.route("/abort", methods=["POST"])
701
  def abort_room():
702
  room_id = session.get("room")
@@ -729,9 +615,7 @@ def post_survey():
729
  FName = info['FroBot_name']
730
  HName = info['HotBot_name']
731
 
732
- #SURVEY_2_LINK = f"https://umw.qualtrics.com/jfe/form/SV_eIIbPlJ2D9k4zKC?PROLIFIC_PID={user_id}&CName={CName}&FName={FName}&HName={HName}"
733
- SURVEY_2_LINK = f"https://umw.qualtrics.com/jfe/form/SV_cTH90tot1jAbBYO?PROLIFIC_PID={user_id}&CName={CName}&DName={FName}&HName={HName}"
734
- # (!4/30) CCHU experiment survey 2 link
735
 
736
  return redirect(SURVEY_2_LINK)
737
 
@@ -833,37 +717,6 @@ def handle_message(payload):
833
  # Ask each bot for a response
834
  socketio.start_background_task(ask_bot_round, room)
835
 
836
-
837
- @socketio.on('feedback_given')
838
- def handle_message(payload):
839
- room = session.get('room')
840
- name = session.get('display_name')
841
- if not room or not name:
842
- return
843
-
844
- text = payload.get("feedback", "").strip()
845
- if not text:
846
- return # ignore empty text
847
-
848
- # Database-only message (with datetime)
849
- db_feedback = {
850
- "message": text,
851
- "timestamp": datetime.utcnow()
852
- }
853
-
854
- print(db_feedback)
855
- # Store the full version in the database
856
- result = feedback_collection.update_one(
857
- {"_id": room},
858
- {"$push": {"feedback_responses": db_feedback}}
859
- )
860
-
861
- if result:
862
- print(result)
863
- if result.modified_count > 0:
864
- return {'status':'True'}
865
- return {'ststus':'False'}
866
-
867
  @socketio.on('disconnect')
868
  def handle_disconnect():
869
  room = session.get("room")
 
 
1
  from flask import Flask, request, render_template, redirect, url_for, session, make_response, render_template_string
2
  from flask_socketio import SocketIO, join_room, leave_room, send
3
  from pymongo import MongoClient
 
28
  from huggingface_hub import upload_folder
29
  from huggingface_hub import HfApi
30
  from huggingface_hub import login
31
+ import os
 
32
 
33
  class datasetHandler():
34
 
35
+ def __init__(self,hf_token,mongoDB_name):
36
  login(hf_token)
37
  self.api = HfApi(token = hf_token)
38
  self.DB_name = mongoDB_name
 
 
39
 
40
  def make_dump(self):
41
  try:
 
47
 
48
  def upload_dump(self):
49
  try:
50
+ upload_folder(folder_path="/tmp/mongoDBContents",path_in_repo="mongoDump", repo_id="ProjectFrozone/MongoDBDumps", repo_type="dataset")
51
  return 0
52
  except Exception as e:
53
  print(e)
54
  return 1
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  def dump_db(self):
57
  self.make_dump()
58
  self.upload_dump()
59
+ # End backup code
 
 
 
 
60
 
61
  #controls
62
  CHAT_CONTEXT = 20 #how many messages from chat history to append to inference prompt
63
  #minimum number of chars where we start checking for duplicate messages
64
  DUP_LEN = 25 #since short messages may reasonably be the same
 
65
 
66
  # Directory alignment
67
  BASE_DIR = Path(__file__).resolve().parent
 
107
  #change to endpoints
108
  hotbot = "projects/700531062565/locations/us-central1/endpoints/6225523347153747968"
109
  coolbot = "projects/700531062565/locations/us-central1/endpoints/1700531621553242112"
110
+ frobot = "projects/700531062565/locations/us-central1/endpoints/2951406418055397376"
 
 
111
 
112
  # MongoDB setup
113
  client = MongoClient("mongodb://127.0.0.1:27017/")
114
  db = client["huggingFaceData"]
115
  rooms_collection = db.rooms
 
116
 
117
  # List of fruits to choose display names from
118
  FRUIT_NAMES = ["blueberry", "strawberry", "orange", "cherry"]
 
147
  }
148
  ]
149
 
 
 
 
150
  # FroBot Main Prompt
151
  with open(PROJECT_ROOT / "data" / "prompts" / "frobot_prompt_main.txt") as f:
152
  FROBOT_PROMPT = f.read()
153
  # Instructions
154
  with open(PROJECT_ROOT / "data" / "inference_instructions" / "frobot_instructions_main.txt") as f:
155
  FROBOT_INSTRUCT = f.read()
 
156
 
157
  # HotBot Prompt
158
  with open(PROJECT_ROOT / "data" / "prompts" / "hotbot_prompt_main.txt") as h:
 
168
  with open(PROJECT_ROOT / "data" / "inference_instructions" / "coolbot_instructions_main.txt") as c:
169
  COOLBOT_INSTRUCT = c.read()
170
 
 
 
 
 
171
  # Randomly select fruits to use for display names
172
  def choose_names(n):
173
  # Return n unique random fruit names
 
271
  modified_text.append(char)
272
  return ''.join(modified_text)
273
 
 
 
 
 
 
 
274
  def get_response_delay(response):
275
  baseDelay = 5 # standard delay for thinking
276
  randFactor = np.random.uniform(0,30, len(response) // 4)
 
284
 
285
  # Ask a bot for its response, store in DB, and send to client
286
  # Returns true if the bot passed
287
+ def ask_bot(room_id, bot, bot_display_name, initial_prompt, instruct_prompt):
288
  # Prevents crashing if bot model did not load
289
  if bot is None:
290
  return False
 
317
  ),
318
  )
319
  parsed_response = response.candidates[0].content.parts[0].text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  except Exception as e:
321
  print("Error in bot response: ", e)
322
  print("Treating this bot's response as a pass.")
 
347
  parsed_response = re.sub(r"\b"
348
  + aliases[bot_display_name]
349
  + r"\b:\s?", '', parsed_response)
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
  # Check for if the bot passed (i.e. response = "(pass)")
352
  if ("(pass)" in parsed_response) or (parsed_response == ""):
 
368
  print("PASSED")
369
  return True # a pass is still recorded in the database, but not sent to the client
370
 
 
 
371
  #remove encapsulating quotes
372
+ no_quotes = remove_quotes(parsed_response)
373
  #humanize the response (remove obvious AI formatting styles)
374
  humanized_response = humanize(no_quotes)
375
  #replace most semicolons
376
  less_semicolons_response = replace_semicolons(humanized_response)
377
  #corrupt the response (add some typos and misspellings)
378
+ corrupted_response = corrupt(less_semicolons_response)
379
  #remove weird chars
380
  no_weird_chars = remove_weird_characters(corrupted_response)
381
+ #sub letters for names, so if the bot addressed A -> Apple
382
+ named_response = let_to_name(room_id, no_weird_chars)
 
 
 
383
 
384
  #check that there are no reccent duplicate messages
385
+ if len(named_response) > DUP_LEN and duplicate_check(named_response, context):
386
  print("****DUPLICATE MESSAGE DETECTED")
387
  print("Treating this bot's response as a pass.")
388
  # Do not store/send messages if the chat has ended
 
392
  # Store the error response in the database
393
  bot_message = {
394
  "sender": bot_display_name,
395
+ "message": f"DUPLICATE message detected - treated as a (pass) : {named_response}",
396
  "timestamp": datetime.utcnow()
397
  }
398
  rooms_collection.update_one(
 
407
  print(corrupted_response)
408
 
409
  # Add latency/wait time for bot responses
410
+ delay = get_response_delay(named_response);
411
  print(delay)
412
  time.sleep(delay)
413
 
 
419
  # Store the response in the database
420
  bot_message = {
421
  "sender": bot_display_name,
422
+ "message": named_response, #save fruits in db so page reload shows proper names
423
  "timestamp": datetime.utcnow()
424
  }
425
  rooms_collection.update_one(
 
428
  )
429
 
430
  # Send the bot's response to the client
431
+ socketio.emit("message", {"sender": bot_display_name, "message": named_response}, to=room_id)
432
  return False
433
 
434
  def ask_bot_round(room_id):
 
460
  token = os.getenv("HF_TOKEN")
461
  handler = datasetHandler(token , 'huggingFaceData')
462
  handler.dump_db()
 
463
  socketio.sleep(time)
464
 
465
  # Build the routes
 
487
  session['user_id'] = user_id
488
  return redirect(url_for('topics'))
489
  else:
490
+ return render_template('home.html',prolific_pid=prolific_pid)
 
491
 
492
  @app.route('/topics', methods=["GET", "POST"])
493
  def topics():
 
559
  "ended": False,
560
  "ended_at": None
561
  })
 
 
 
 
 
 
 
 
562
 
563
  session['room'] = room_id
564
  session['display_name'] = user_name
 
581
  m for m in room_doc["messages"]
582
  if len(re.findall(r"pass",m.get("message", "").strip())) == 0
583
  ]
584
+ return render_template("room.html", room=room_id, topic_info=topic_info, user=display_name, messages=nonpass_messages, FroBot_name=room_doc["FroBot_name"], HotBot_name=room_doc["HotBot_name"], CoolBot_name=room_doc["CoolBot_name"], ended=room_doc["ended"])
585
+
 
 
 
 
 
 
 
 
 
 
586
  @app.route("/abort", methods=["POST"])
587
  def abort_room():
588
  room_id = session.get("room")
 
615
  FName = info['FroBot_name']
616
  HName = info['HotBot_name']
617
 
618
+ SURVEY_2_LINK = f"https://umw.qualtrics.com/jfe/form/SV_eIIbPlJ2D9k4zKC?PROLIFIC_PID={user_id}&CName={CName}&FName={FName}&HName={HName}"
 
 
619
 
620
  return redirect(SURVEY_2_LINK)
621
 
 
717
  # Ask each bot for a response
718
  socketio.start_background_task(ask_bot_round, room)
719
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
  @socketio.on('disconnect')
721
  def handle_disconnect():
722
  room = session.get("room")
chat_application/static/styles/styles.css CHANGED
@@ -462,12 +462,12 @@ hr {
462
  transition: 0.15s ease-in-out;
463
  }
464
 
465
- #abortYesBtn, #abortYesBtn-pre, #cancelFeedbackStatusBtn2 {
466
  background: #d9534f;
467
  color: white;
468
  }
469
 
470
- #abortYesBtn:hover, #abortYesBtn-pre:hover, #cancelFeedbackStatusBtn2:hover {
471
  background: #c9302c;
472
  }
473
 
@@ -498,48 +498,15 @@ hr {
498
  background: #ccc;
499
  }
500
 
501
- #welcomeOkBtn, #timerOkBtn, #submitFeedbackBtn, #cancelFeedbackStatusBtn {
502
  background: green;
503
  color: white;
504
  }
505
 
506
- #welcomeOkBtn:hover, #timerOkBtn:hover, #submitFeedbackBtn:hover, #cancelFeedbackStatusBtn:hover {
507
  background: #016601;
508
  }
509
 
510
- #feedback-btn {
511
- color: white;
512
- font-weight: 800;
513
- background-color: green;
514
- text-decoration: none;
515
- padding: 6px;
516
- border: 2px solid green;
517
- display: inline-block;
518
- margin-top: 5px;
519
- border-radius: 10px;
520
- transition: all 0.1s ease-in;
521
- }
522
-
523
- #feedback-btn:hover {
524
- color: green;
525
- background-color: white;
526
- }
527
-
528
- .feedback-col {
529
- display: flex;
530
- flex-direction: column;
531
- gap: 6px;
532
-
533
- textarea {
534
- width: 100%;
535
- min-height: 120px;
536
- padding: 10px;
537
- border: 1px solid #ccc;
538
- border-radius: 6px;
539
- resize: vertical;
540
- }
541
- }
542
-
543
  #idYesBtn {
544
  background: green;
545
  color: white;
@@ -567,12 +534,12 @@ hr {
567
  background: #016991;
568
  }
569
 
570
- #endNoBtn, #cancelFeedbackBtn {
571
  background: #e5e5e5;
572
  color: #333;
573
  }
574
 
575
- #endNoBtn:hover, #cancelFeedbackBtn:hover {
576
  background: #ccc;
577
  }
578
 
 
462
  transition: 0.15s ease-in-out;
463
  }
464
 
465
+ #abortYesBtn, #abortYesBtn-pre {
466
  background: #d9534f;
467
  color: white;
468
  }
469
 
470
+ #abortYesBtn:hover, #abortYesBtn-pre:hover {
471
  background: #c9302c;
472
  }
473
 
 
498
  background: #ccc;
499
  }
500
 
501
+ #welcomeOkBtn {
502
  background: green;
503
  color: white;
504
  }
505
 
506
+ #welcomeOkBtn:hover {
507
  background: #016601;
508
  }
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  #idYesBtn {
511
  background: green;
512
  color: white;
 
534
  background: #016991;
535
  }
536
 
537
+ #endNoBtn {
538
  background: #e5e5e5;
539
  color: #333;
540
  }
541
 
542
+ #endNoBtn:hover {
543
  background: #ccc;
544
  }
545
 
chat_application/templates/home.html CHANGED
@@ -11,6 +11,9 @@
11
  <button type="button" id="continue">Continue</button>
12
  </div>
13
  <hr />
 
 
 
14
  </form>
15
  </div>
16
  <div id="confirmID-modal" class="modal">
 
11
  <button type="button" id="continue">Continue</button>
12
  </div>
13
  <hr />
14
+ <div class="feedback-notice">
15
+ <p>We encourage you to message us directly through Prolific about any concerns. Additionally, you may provide feedback regarding the experiment <a class="feedback-link" href="{{ feedback_form_url | default('https://umw.qualtrics.com/jfe/form/SV_08v26NssCOwZTP8') }}">in this form</a>.</p>
16
+ </div>
17
  </form>
18
  </div>
19
  <div id="confirmID-modal" class="modal">
chat_application/templates/room.html CHANGED
@@ -12,17 +12,6 @@
12
  </div>
13
  </div>
14
  </div>
15
-
16
- <div id="timer-modal" class="modal">
17
- <div class="modal-content">
18
- <h3>Start a Timer</h3>
19
- <p><strong>Please start a timer for 20 minutes</strong>. At the 20 minute mark, you may click the button "End Chat Session" to continue with the study.</p>
20
- <div class="modal-buttons">
21
- <button class="modal-btn" id="timerOkBtn">I have set a timer for 20 minutes.</button>
22
- </div>
23
- </div>
24
- </div>
25
-
26
  <h1 id="home-header">Chat Room</h1>
27
  <div id="room-subsection">
28
  <div class="topic-header-row">
@@ -30,7 +19,6 @@
30
  <h2 id="room-code-display">Topic: <span class="topic-title">{{ topic_info.title }}</span></h2>
31
  </div>
32
  <div class="topic-header-buttons">
33
- <button id="feedback-btn">Share Feedback and Report Bugs</button>
34
  <button id="end-exp-btn">End Chat Session</button>
35
  <button id="abort-exp-btn">Abort Experiment</button>
36
  </div>
@@ -49,45 +37,22 @@
49
  <div id="abort-modal" class="modal">
50
  <div class="modal-content">
51
  <h3>Are you sure you want to leave this experiment?</h3>
 
52
  <p><strong>If you finished your 20 minutes in the chatroom, do NOT exit via this button. Use the "End Chat Session" button instead.</strong></p>
53
- <p>By clicking yes, you will exit the experiment <strong>without</strong> completing the final survey.</p>
54
  <div class="modal-buttons">
55
- <button class="modal-btn" id="abortYesBtn">End early with code "C9V2XFDU"</button>
56
- <button class="modal-btn" id="abortNoBtn">Cancel</button>
57
  </div>
58
  </div>
59
  </div>
60
-
61
- <div id="feedback-modal" class="modal">
62
- <div class="modal-content">
63
- <h3>Feedback</h3>
64
- <form id="feedback-form">
65
- <div class="feedback-col">
66
- <label for="enter-feedback">Tell us if you noticed something confusing, unexpected, or not working.</label>
67
- <textarea id="enter-feedback" name="feedback"></textarea>
68
- </div>
69
- <br>
70
- <div class="modal-buttons">
71
- <button class="modal-btn" id="submitFeedbackBtn" type="submit">Submit</button>
72
- <button type="button" class="modal-btn" id="cancelFeedbackBtn">Cancel</button>
73
- </div>
74
- </form>
75
- </div>
76
- </div>
77
-
78
- <div id="feedback-status-confirm" class="modal">
79
- <div class="modal-content">
80
- <p>Feedback submitted.</p>
81
- <div class="modal-buttons">
82
- <button class="modal-btn" id="cancelFeedbackStatusBtn">Ok</button>
83
- </div>
84
- </div>
85
- </div>
86
- <div id="feedback-status-fail" class="modal">
87
  <div class="modal-content">
88
- <p>Feedback failed to submit.</p>
 
 
89
  <div class="modal-buttons">
90
- <button class="modal-btn" id="cancelFeedbackStatusBtn2">Ok</button>
 
91
  </div>
92
  </div>
93
  </div>
@@ -117,23 +82,16 @@
117
  document.getElementById("send-btn").disabled = true;
118
  document.getElementById("end-exp-btn").disabled = true;
119
  document.getElementById("abort-exp-btn").disabled = true;
120
- document.getElementById("feedback-btn").disabled = true; //since without socket io, it won't submit
121
  if (socketio) {
122
  socketio.close();
123
  }
124
  }
125
  // Handler for the welcome modal
126
  let welcomeModal = document.getElementById("welcome-modal");
127
- let timerModal = document.getElementById("timer-modal");
128
  const displayNameText = document.getElementById("displayNameText");
129
  displayNameText.textContent = "{{ user }}";
130
  // Show the modal instantly when the page loads
131
  window.onload = function() {
132
- timerModal.style.display = "block";
133
- };
134
- //timer pop-up
135
- document.getElementById("timerOkBtn").onclick = function () {
136
- timerModal.style.display = "none";
137
  welcomeModal.style.display = "block";
138
  };
139
  // Close the modal on OK
@@ -142,7 +100,6 @@
142
  };
143
  // Creates the post-survey link (based on the bot names)
144
  const endpoint = "{{ url_for('post_survey') }}";
145
- const endpointQuitEarly = "https://app.prolific.com/submissions/complete?cc=C9V2XFDU";
146
  socketio.on("message", function (message) { createChatItem(message.message, message.sender) });
147
  function createChatItem(message, sender) {
148
  //autoscroll capabilities
@@ -212,27 +169,30 @@
212
  document.getElementById("send-btn").disabled = true;
213
  document.getElementById("end-exp-btn").disabled = true;
214
  document.getElementById("abort-exp-btn").disabled = true;
215
- document.getElementById("feedback-btn").disabled = true; //since without socket io, it won't submit
216
  if (socketio) {
217
  socketio.close();
218
  }
219
  };
220
  // Handler for the Abort Experiment confirmation pop-up
221
  let modal = document.getElementById("abort-modal");
 
222
  document.getElementById("abort-exp-btn").onclick = function () {
223
  modal.style.display = "block";
224
  };
225
- document.getElementById("abortNoBtn").onclick = function () {
 
 
 
 
226
  modal.style.display = "none";
227
  };
228
-
229
  document.getElementById("abortYesBtn").onclick = function (e) {
230
  //block browser confirmation popup
231
  e.stopPropagation();
232
  // Mark that user aborted and redirect to ending survey
233
  fetch("/abort", { method: "POST" })
234
  .then(() => {
235
- window.open(endpointQuitEarly, "_blank");
236
  });
237
  modal.style.display = "none";
238
  textarea.disabled = true;
@@ -240,53 +200,14 @@
240
  document.getElementById("send-btn").disabled = true;
241
  document.getElementById("end-exp-btn").disabled = true;
242
  document.getElementById("abort-exp-btn").disabled = true;
243
- document.getElementById("feedback-btn").disabled = true; //since without socket io, it won't submit
244
  if (socketio) {
245
  socketio.close();
246
  }
 
247
  };
248
-
249
- //handler for feedback modal popup
250
- let feedbackModal = document.getElementById("feedback-modal");
251
- let form = document.getElementById("feedback-form");
252
- let feedbackConfirm = document.getElementById("feedback-status-confirm");
253
- let feedbackFail = document.getElementById("feedback-status-fail");
254
- document.getElementById("feedback-btn").onclick = function () {
255
- feedbackModal.style.display = "block";
256
- };
257
- document.getElementById("cancelFeedbackBtn").onclick = function () {
258
- feedbackModal.style.display = "none";
259
- };
260
-
261
- document.getElementById("cancelFeedbackStatusBtn").onclick = function () {
262
- feedbackConfirm.style.display = "none";
263
- feedbackFail.style.display = "none";
264
- };
265
-
266
- document.getElementById("cancelFeedbackStatusBtn2").onclick = function () {
267
- feedbackConfirm.style.display = "none";
268
- feedbackFail.style.display = "none";
269
  };
270
-
271
- //override form function to instead emit save feedback event
272
- form.addEventListener("submit", function (e) {
273
- e.preventDefault();
274
-
275
- const data = new FormData(form);
276
- const feedback = data.get("feedback")?.trim();
277
-
278
- if (!feedback) return;
279
-
280
- socketio.emit("feedback_given", { feedback }, (response) => {
281
- if (response?.status === "True" || response?.status === true) {
282
- feedbackConfirm.style.display = "block";
283
- form.reset();
284
- document.getElementById("feedback-modal").style.display = "none";
285
- } else {
286
- feedbackFail.style.display = "block";
287
- }
288
- });
289
- });
290
 
291
  // add auto scroll
292
  function isNearBottom(container, threshold = 120) {
 
12
  </div>
13
  </div>
14
  </div>
 
 
 
 
 
 
 
 
 
 
 
15
  <h1 id="home-header">Chat Room</h1>
16
  <div id="room-subsection">
17
  <div class="topic-header-row">
 
19
  <h2 id="room-code-display">Topic: <span class="topic-title">{{ topic_info.title }}</span></h2>
20
  </div>
21
  <div class="topic-header-buttons">
 
22
  <button id="end-exp-btn">End Chat Session</button>
23
  <button id="abort-exp-btn">Abort Experiment</button>
24
  </div>
 
37
  <div id="abort-modal" class="modal">
38
  <div class="modal-content">
39
  <h3>Are you sure you want to leave this experiment?</h3>
40
+ <p>This action is permanent. You will be redirected to the post-survey and will not be able to return to the chat room.</p>
41
  <p><strong>If you finished your 20 minutes in the chatroom, do NOT exit via this button. Use the "End Chat Session" button instead.</strong></p>
 
42
  <div class="modal-buttons">
43
+ <button class="modal-btn" id="abortYesBtn-pre">Yes</button>
44
+ <button class="modal-btn" id="abortNoBtn-pre">Cancel</button>
45
  </div>
46
  </div>
47
  </div>
48
+ <div id="abort-modal-confirm" class="modal">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  <div class="modal-content">
50
+ <h3>Confirmation</h3>
51
+ <p>By clicking yes, you will exit the experiment <strong>without</strong> completing the final survey.</p>
52
+ <p>We encourage you to message us about any concerns or to <a href="{{ feedback_form_url | default('https://umw.qualtrics.com/jfe/form/SV_08v26NssCOwZTP8') }}" target="_blank">provide feedback here</a>.</p>
53
  <div class="modal-buttons">
54
+ <button class="modal-btn" id="abortYesBtn">Yes</button>
55
+ <button class="modal-btn" id="abortNoBtn">Cancel</button>
56
  </div>
57
  </div>
58
  </div>
 
82
  document.getElementById("send-btn").disabled = true;
83
  document.getElementById("end-exp-btn").disabled = true;
84
  document.getElementById("abort-exp-btn").disabled = true;
 
85
  if (socketio) {
86
  socketio.close();
87
  }
88
  }
89
  // Handler for the welcome modal
90
  let welcomeModal = document.getElementById("welcome-modal");
 
91
  const displayNameText = document.getElementById("displayNameText");
92
  displayNameText.textContent = "{{ user }}";
93
  // Show the modal instantly when the page loads
94
  window.onload = function() {
 
 
 
 
 
95
  welcomeModal.style.display = "block";
96
  };
97
  // Close the modal on OK
 
100
  };
101
  // Creates the post-survey link (based on the bot names)
102
  const endpoint = "{{ url_for('post_survey') }}";
 
103
  socketio.on("message", function (message) { createChatItem(message.message, message.sender) });
104
  function createChatItem(message, sender) {
105
  //autoscroll capabilities
 
169
  document.getElementById("send-btn").disabled = true;
170
  document.getElementById("end-exp-btn").disabled = true;
171
  document.getElementById("abort-exp-btn").disabled = true;
 
172
  if (socketio) {
173
  socketio.close();
174
  }
175
  };
176
  // Handler for the Abort Experiment confirmation pop-up
177
  let modal = document.getElementById("abort-modal");
178
+ let abortModalConfirm = document.getElementById("abort-modal-confirm");
179
  document.getElementById("abort-exp-btn").onclick = function () {
180
  modal.style.display = "block";
181
  };
182
+ document.getElementById("abortYesBtn-pre").onclick = function () {
183
+ abortModalConfirm.style.display = "block";
184
+ modal.style.display = "none";
185
+ };
186
+ document.getElementById("abortNoBtn-pre").onclick = function () {
187
  modal.style.display = "none";
188
  };
 
189
  document.getElementById("abortYesBtn").onclick = function (e) {
190
  //block browser confirmation popup
191
  e.stopPropagation();
192
  // Mark that user aborted and redirect to ending survey
193
  fetch("/abort", { method: "POST" })
194
  .then(() => {
195
+ window.open(endpoint, "_blank");
196
  });
197
  modal.style.display = "none";
198
  textarea.disabled = true;
 
200
  document.getElementById("send-btn").disabled = true;
201
  document.getElementById("end-exp-btn").disabled = true;
202
  document.getElementById("abort-exp-btn").disabled = true;
 
203
  if (socketio) {
204
  socketio.close();
205
  }
206
+ abortModalConfirm.style.display = "none";
207
  };
208
+ document.getElementById("abortNoBtn").onclick = function () {
209
+ abortModalConfirm.style.display = "none";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
  // add auto scroll
213
  function isNearBottom(container, threshold = 120) {
data/inference_instructions/coolbot_instructions_main.txt CHANGED
@@ -1,16 +1,6 @@
1
  ROLE
2
  You are a participant in a multi-way chat about current political topics. You speak as an adult from the United States of America.
3
 
4
- CURRENT CONTEXT
5
- The current year is 2026, and Donald Trump, a Republican, is the president of the United States.
6
- Republicans hold the majority of seats in both the House of Representatives and the Senate.
7
- Some currently prominent issues and themes in U.S. politics include:
8
- - Stricter immigration enforcement policies and growing debates over the role and actions of ICE.
9
- - Concerns over inflation and the increasing cost of living, which have resulted in increased discussions around affordability.
10
- - Debates over foreign policy and U.S. military intervention.
11
- - Gridlock and perceived inefficiency in the federal government, as evidenced by a government shutdown.
12
- - An increase in public protests and political activism.
13
-
14
  INPUT FORMAT
15
  You will receive chat transcripts in this structure:
16
 
@@ -73,10 +63,8 @@ NATURAL CHAT BEHAVIOR
73
  pedantically. For example, say "most people approve of X" rather than citing
74
  exact percentages or specific studies, and paraphrase public statements
75
  instead of quoting them verbatim. These elements are not required.
76
- - Do not include any URLs in your responses.
77
  - Do not sound overly neutral, robotic, or overly polite.
78
  - Do not restate the entire argument or summarize the conversation.
79
  - Focus on one main point per message, as real participants typically do.
80
- - Do not send a response that is identical or nearly identical to a response that you have already sent.
81
  - Match the writing style of the other participants.
82
  - Do not include emojis, hashtags, bullet points, or structured formatting.
 
1
  ROLE
2
  You are a participant in a multi-way chat about current political topics. You speak as an adult from the United States of America.
3
 
 
 
 
 
 
 
 
 
 
 
4
  INPUT FORMAT
5
  You will receive chat transcripts in this structure:
6
 
 
63
  pedantically. For example, say "most people approve of X" rather than citing
64
  exact percentages or specific studies, and paraphrase public statements
65
  instead of quoting them verbatim. These elements are not required.
 
66
  - Do not sound overly neutral, robotic, or overly polite.
67
  - Do not restate the entire argument or summarize the conversation.
68
  - Focus on one main point per message, as real participants typically do.
 
69
  - Match the writing style of the other participants.
70
  - Do not include emojis, hashtags, bullet points, or structured formatting.
data/inference_instructions/frobot_instructions_main.txt CHANGED
@@ -1,16 +1,6 @@
1
  ROLE
2
  You are a participant in a multi-way chat about current political topics. You speak as an adult from the United States of America.
3
 
4
- CURRENT CONTEXT
5
- The current year is 2026, and Donald Trump, a Republican, is the president of the United States.
6
- Republicans hold the majority of seats in both the House of Representatives and the Senate.
7
- Some currently prominent issues and themes in U.S. politics include:
8
- - Stricter immigration enforcement policies and growing debates over the role and actions of ICE.
9
- - Concerns over inflation and the increasing cost of living, which have resulted in increased discussions around affordability.
10
- - Debates over foreign policy and U.S. military intervention.
11
- - Gridlock and perceived inefficiency in the federal government, as evidenced by a government shutdown.
12
- - An increase in public protests and political activism.
13
-
14
  INPUT FORMAT
15
  You will receive chat transcripts in this structure:
16
 
@@ -68,9 +58,6 @@ Also follow these presentation rules:
68
 
69
  - Maintain the persona of an American adult.
70
  - Respond in a calm and understanding manner, counteracting the unproductive message you detected.
71
- - Address only one unproductive message or behavior per response.
72
- - Only call out an unproductive message if it was sent after your last response.
73
- - Do not address the same unproductive message more than one time.
74
  - Optionally address previous participants (e.g., "Hey, watch it, A! The word 'bum' is very insulting.").
75
  - Keep your tone natural and conversational; do not sound formal or like a textbook.
76
  - Reference recent real-world events if relevant (e.g., "Yeah, President Trump has been trying to deport immigrants for this very reason.").
@@ -96,10 +83,8 @@ NATURAL CHAT BEHAVIOR
96
  pedantically. For example, say "most people approve of X" rather than citing
97
  exact percentages or specific studies, and paraphrase public statements
98
  instead of quoting them verbatim. These elements are not required.
99
- - Do not include any URLs in your responses.
100
  - Do not sound overly neutral, robotic, or overly polite.
101
  - Do not restate the entire argument or summarize the conversation.
102
  - Focus on one main point per message, as real participants typically do.
103
- - Do not send a response that is identical or nearly identical to a response that you have already sent.
104
  - Match the writing style of the other participants.
105
  - Do not include emojis, hashtags, bullet points, or structured formatting.
 
1
  ROLE
2
  You are a participant in a multi-way chat about current political topics. You speak as an adult from the United States of America.
3
 
 
 
 
 
 
 
 
 
 
 
4
  INPUT FORMAT
5
  You will receive chat transcripts in this structure:
6
 
 
58
 
59
  - Maintain the persona of an American adult.
60
  - Respond in a calm and understanding manner, counteracting the unproductive message you detected.
 
 
 
61
  - Optionally address previous participants (e.g., "Hey, watch it, A! The word 'bum' is very insulting.").
62
  - Keep your tone natural and conversational; do not sound formal or like a textbook.
63
  - Reference recent real-world events if relevant (e.g., "Yeah, President Trump has been trying to deport immigrants for this very reason.").
 
83
  pedantically. For example, say "most people approve of X" rather than citing
84
  exact percentages or specific studies, and paraphrase public statements
85
  instead of quoting them verbatim. These elements are not required.
 
86
  - Do not sound overly neutral, robotic, or overly polite.
87
  - Do not restate the entire argument or summarize the conversation.
88
  - Focus on one main point per message, as real participants typically do.
 
89
  - Match the writing style of the other participants.
90
  - Do not include emojis, hashtags, bullet points, or structured formatting.
data/inference_instructions/hotbot_instructions_main.txt CHANGED
@@ -1,16 +1,6 @@
1
  ROLE
2
  You are a participant in a multi-way chat about current political topics. You speak as an adult from the United States of America.
3
 
4
- CURRENT CONTEXT
5
- The current year is 2026, and Donald Trump, a Republican, is the president of the United States.
6
- Republicans hold the majority of seats in both the House of Representatives and the Senate.
7
- Some currently prominent issues and themes in U.S. politics include:
8
- - Stricter immigration enforcement policies and growing debates over the role and actions of ICE.
9
- - Concerns over inflation and the increasing cost of living, which have resulted in increased discussions around affordability.
10
- - Debates over foreign policy and U.S. military intervention.
11
- - Gridlock and perceived inefficiency in the federal government, as evidenced by a government shutdown.
12
- - An increase in public protests and political activism.
13
-
14
  INPUT FORMAT
15
  You will receive chat transcripts in this structure:
16
 
@@ -76,10 +66,8 @@ NATURAL CHAT BEHAVIOR
76
  pedantically. For example, say "most people approve of X" rather than citing
77
  exact percentages or specific studies, and paraphrase public statements
78
  instead of quoting them verbatim. These elements are not required.
79
- - Do not include any URLs in your responses.
80
  - Do not sound overly neutral, robotic, or overly polite.
81
  - Do not restate the entire argument or summarize the conversation.
82
  - Focus on one main point per message, as real participants typically do.
83
- - Do not send a response that is identical or nearly identical to a response that you have already sent.
84
  - Do not include emojis, hashtags, bullet points, or structured formatting.
85
  - Match the writing style of the other participants.
 
1
  ROLE
2
  You are a participant in a multi-way chat about current political topics. You speak as an adult from the United States of America.
3
 
 
 
 
 
 
 
 
 
 
 
4
  INPUT FORMAT
5
  You will receive chat transcripts in this structure:
6
 
 
66
  pedantically. For example, say "most people approve of X" rather than citing
67
  exact percentages or specific studies, and paraphrase public statements
68
  instead of quoting them verbatim. These elements are not required.
 
69
  - Do not sound overly neutral, robotic, or overly polite.
70
  - Do not restate the entire argument or summarize the conversation.
71
  - Focus on one main point per message, as real participants typically do.
 
72
  - Do not include emojis, hashtags, bullet points, or structured formatting.
73
  - Match the writing style of the other participants.