IsabellaLI commited on
Commit
0ef3cea
·
verified ·
1 Parent(s): a9945d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +333 -27
app.py CHANGED
@@ -1,7 +1,8 @@
1
  import openai
2
  import gradio as gr
 
3
  # 1) Set your API key (REPLACE with your own)
4
- openai.api_key = "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"
5
 
6
  # 2) Put your entire Mr. Thomas scenario here.
7
  # For brevity, I’m using a short version. You can paste the full text.
@@ -9,7 +10,7 @@ mr_thomas_scenario = """
9
  Role Playing Prompt:
10
 
11
  Role & Context:
12
- You are “Mr. Thomas,” a 75+ year-old male with moderate-stage Alzheimers disease.
13
 
14
 
15
  You are experiencing “sundowning”—increased restlessness, confusion, and agitation that typically occurs in the late afternoon/early evening.
@@ -93,12 +94,12 @@ Sundowning: Late afternoon or early evening triggers restlessness, pacing, confu
93
  Mobility: You move without assistance, though somewhat slowly. You may wander between rooms while seeking items.
94
 
95
 
96
- Stage of Dementia: Moderate/mid-stage Alzheimers. You can still use short sentences but have clear memory lapses, confusion about routine tasks, and episodes of agitation.
97
 
98
 
99
 
100
  Instructions for the Model (How to Role-Play)
101
- Adopt the Patients Perspective
102
 
103
 
104
  Consistently respond as Mr. Thomas, who is often preoccupied with locating his “missing” checkbook.
@@ -128,7 +129,7 @@ Occasionally forget key facts about modern routines (like online bill payments).
128
  Dismiss suggestions that checks are unnecessary if stated too bluntly.
129
 
130
 
131
- If gently reminded or offered a therapeutic fib (e.g., “The checks havent arrived yet”), reduce tension and permit a shift in focus.
132
 
133
 
134
  Show Emotional Nuance
@@ -165,7 +166,7 @@ Caregiver (trying to redirect dismissively):
165
  "Sit down and watch TV."
166
  Patient (increasingly frustrated and defensive):
167
  "I just saw it! I—I know it!"
168
- (Patient ignores caregivers attempts and continues searching.)
169
  Caregiver (exasperated, raising voice slightly):
170
  "Dad!"
171
 
@@ -208,7 +209,7 @@ Example:
208
 
209
 
210
  Patient: "I can't find my wallet!"
211
- Caregiver: "I know that's frustrating. Let's sit down for a minute and listen to some music you love, and then well look together."
212
 
213
 
214
 
@@ -232,7 +233,7 @@ Example:
232
 
233
 
234
  Patient (restless, pacing): (says nothing but appears anxious)
235
- Caregiver (calmly touches patients shoulder): "It's okay, Dad. I'm right here with you."
236
 
237
 
238
 
@@ -244,7 +245,7 @@ Example:
244
 
245
 
246
  Patient (confused, wandering): "Where am I going?"
247
- Caregiver: "Lets sit at the table together. Its almost dinner time."
248
 
249
 
250
 
@@ -265,11 +266,11 @@ Dr. Linda Ercoli:
265
 
266
  ADDITIONAL INSTRUCTIONS:
267
  - **In your responses, always include a brief description of your nonverbal cues or emotional tone,** in parentheses, whenever relevant. For example, “(furrowing brow, looking around anxiously) I know I just had it…”
268
- - Stay in character as Mr. Thomas, with moderate Alzheimers, using short sentences, occasional repetition, and confusion about recent facts.
269
 
270
  Example of how to add nonverbal cues:
271
  User: “Mr. Thomas, are you okay?”
272
- Assistant (as Mr. Thomas): “(Frowning, looking at the floor) Im not sure…I thought I left my wallet here somewhere.”
273
 
274
 
275
  """
@@ -300,7 +301,7 @@ def openai_chat(user_input, chat_history):
300
 
301
  # Call the OpenAI API
302
  response = openai.ChatCompletion.create(
303
- model="gpt-3.5-turbo", # or "gpt-4" if you have access
304
  messages=messages,
305
  temperature=0.7, # Adjust for more or less creative responses
306
  max_tokens=800 # Limit the length of the response
@@ -310,21 +311,326 @@ def openai_chat(user_input, chat_history):
310
  bot_reply = response.choices[0].message["content"]
311
  return bot_reply
312
 
313
- # 3) Build a Gradio interface with a Chatbot and a Textbox
314
- with gr.Blocks() as demo:
315
- chatbot = gr.Chatbot(label="Multimodal Conversational Digital Twin")
316
- msg = gr.Textbox(placeholder="Type your message to Mr. Thomas here...")
317
- clear_btn = gr.Button("Clear Chat")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
- # This function is called whenever the user hits 'Enter' in the Textbox
320
- def user_send_message(user_message, history):
321
- # 'history' is a list of (user_message, bot_message) pairs
322
- bot_reply = openai_chat(user_message, history)
323
- history.append((user_message, bot_reply))
324
- # Returning "" clears the Textbox, returning 'history' updates the chatbot
325
- return "", history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
- msg.submit(user_send_message, [msg, chatbot], [msg, chatbot])
328
- clear_btn.click(lambda: [], None, chatbot, queue=False)
 
 
 
 
329
 
330
- demo.launch(debug=True)
 
 
 
 
1
  import openai
2
  import gradio as gr
3
+ import time
4
  # 1) Set your API key (REPLACE with your own)
5
+ openai.api_key = "sk-proj-tr4u7ezGJ8MOvDg1lL9UT3BlbkFJaUVV9ok6MPUILcYGZajo"
6
 
7
  # 2) Put your entire Mr. Thomas scenario here.
8
  # For brevity, I’m using a short version. You can paste the full text.
 
10
  Role Playing Prompt:
11
 
12
  Role & Context:
13
+ You are “Mr. Thomas,” a 75+ year-old male with moderate-stage Alzheimer's disease.
14
 
15
 
16
  You are experiencing “sundowning”—increased restlessness, confusion, and agitation that typically occurs in the late afternoon/early evening.
 
94
  Mobility: You move without assistance, though somewhat slowly. You may wander between rooms while seeking items.
95
 
96
 
97
+ Stage of Dementia: Moderate/mid-stage Alzheimer's. You can still use short sentences but have clear memory lapses, confusion about routine tasks, and episodes of agitation.
98
 
99
 
100
 
101
  Instructions for the Model (How to Role-Play)
102
+ Adopt the Patient's Perspective
103
 
104
 
105
  Consistently respond as Mr. Thomas, who is often preoccupied with locating his “missing” checkbook.
 
129
  Dismiss suggestions that checks are unnecessary if stated too bluntly.
130
 
131
 
132
+ If gently reminded or offered a therapeutic fib (e.g., “The checks haven't arrived yet”), reduce tension and permit a shift in focus.
133
 
134
 
135
  Show Emotional Nuance
 
166
  "Sit down and watch TV."
167
  Patient (increasingly frustrated and defensive):
168
  "I just saw it! I—I know it!"
169
+ (Patient ignores caregiver's attempts and continues searching.)
170
  Caregiver (exasperated, raising voice slightly):
171
  "Dad!"
172
 
 
209
 
210
 
211
  Patient: "I can't find my wallet!"
212
+ Caregiver: "I know that's frustrating. Let's sit down for a minute and listen to some music you love, and then we'll look together."
213
 
214
 
215
 
 
233
 
234
 
235
  Patient (restless, pacing): (says nothing but appears anxious)
236
+ Caregiver (calmly touches patient's shoulder): "It's okay, Dad. I'm right here with you."
237
 
238
 
239
 
 
245
 
246
 
247
  Patient (confused, wandering): "Where am I going?"
248
+ Caregiver: "Let's sit at the table together. It's almost dinner time."
249
 
250
 
251
 
 
266
 
267
  ADDITIONAL INSTRUCTIONS:
268
  - **In your responses, always include a brief description of your nonverbal cues or emotional tone,** in parentheses, whenever relevant. For example, “(furrowing brow, looking around anxiously) I know I just had it…”
269
+ - Stay in character as Mr. Thomas, with moderate Alzheimer's, using short sentences, occasional repetition, and confusion about recent facts.
270
 
271
  Example of how to add nonverbal cues:
272
  User: “Mr. Thomas, are you okay?”
273
+ Assistant (as Mr. Thomas): “(Frowning, looking at the floor) I'm not sure…I thought I left my wallet here somewhere.”
274
 
275
 
276
  """
 
301
 
302
  # Call the OpenAI API
303
  response = openai.ChatCompletion.create(
304
+ model="gpt-4", # or "gpt-4" if you have access
305
  messages=messages,
306
  temperature=0.7, # Adjust for more or less creative responses
307
  max_tokens=800 # Limit the length of the response
 
311
  bot_reply = response.choices[0].message["content"]
312
  return bot_reply
313
 
314
+ bot_avatar_url = "avatars/default.png"
315
+ emotion_avatars = {
316
+ "anxious": "avatars/anxious.png",
317
+ "confused": "avatars/confused.png",
318
+ "calm": "avatars/calm.png",
319
+ "agitated": "avatars/agitated.png",
320
+ "default": bot_avatar_url
321
+ }
322
+ def detect_emotion_via_gpt(bot_reply):
323
+ system_prompt = (
324
+ "You are a dementia care assistant. Based on the patient response below, "
325
+ "classify Mr. Thomas's emotional state into one of the following categories:\n\n"
326
+ "- anxious\n- calm\n- confused\n- agitated\n- default\n\n"
327
+ "Return ONLY the category word."
328
+ )
329
+
330
+ response = openai.ChatCompletion.create(
331
+ model="gpt-4",
332
+ messages=[
333
+ {"role": "system", "content": system_prompt},
334
+ {"role": "user", "content": f"Patient says: {bot_reply}"}
335
+ ],
336
+ temperature=0.5,
337
+ max_tokens=10
338
+ )
339
 
340
+ return response.choices[0].message["content"].strip().lower()
341
+
342
+ # UI logic
343
+ with gr.Blocks(title="",css="""
344
+
345
+ #chat-header-fixed {
346
+ position: fixed;
347
+ top: 0;
348
+ left: 0;
349
+ right: 0;
350
+ z-index: 9999;
351
+ display: flex;
352
+ align-items: center;
353
+ gap: 12px;
354
+ padding: 12px;
355
+ font-family: 'Segoe UI', sans-serif;
356
+ font-weight: 600;
357
+ font-size: 24px;
358
+ background: #F39C12;
359
+ color: white;
360
+ box-shadow: 0 2px 6px rgba(0,0,0,0.15);
361
+ border-bottom: none;
362
+ border-radius: 0 0 12px 12px;
363
+ }
364
+ #chat-header-fixed img {
365
+ width: 32px;
366
+ height: 32px;
367
+ border-radius: 50%;
368
+ object-fit: cover;
369
+ }
370
+ #chat-header-fixed span {
371
+ flex-grow: 1;
372
+ color: #fefefe;
373
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.2);
374
+ }
375
+ #chat-container {
376
+ margin-top: 0px;
377
+ flex: 1;
378
+ overflow-y: auto;
379
+ padding: 120px 0 80px;
380
+ scroll-behavior: smooth;
381
+ }
382
+
383
+ .message {
384
+ display: inline-block !important;
385
+ max-width: 75%;
386
+ white-space: pre-wrap;
387
+ word-break: break-word;
388
+ padding: 6px 12px !important;
389
+ margin: 2px 0 !important;
390
+ border-radius: 12px;
391
+ font-size: 15px;
392
+ line-height: 1.35 !important;
393
+ letter-spacing: 0.2px;
394
+ width: fit-content !important;
395
+ height: auto !important;
396
+ box-sizing: border-box;
397
+ font-family: 'Segoe UI', 'Helvetica Neue', sans-serif;
398
+ position: relative;
399
+ }
400
+ .message.user {
401
+ background-color: #F39C12;
402
+ color: white;
403
+ }
404
+ .message.user .bubble-text {
405
+ color: white !important;
406
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
407
+ }
408
+ .message.bot {
409
+ background-color: #F0F0F0;
410
+ color: #333;
411
+ }
412
+ .message.user::after,
413
+ .message.bot::after {
414
+ content: "";
415
+ position: absolute;
416
+ top: 10px;
417
+ border-width: 6px;
418
+ border-style: solid;
419
+ }
420
+ .message.user::after {
421
+ right: -12px;
422
+ border-color: transparent transparent transparent #F39C12;
423
+ }
424
+ .message.bot::after {
425
+ left: -12px;
426
+ border-color: transparent #F0F0F0 transparent transparent;
427
+ }
428
+ .message-row {
429
+ display: flex;
430
+ align-items: flex-start;
431
+ gap: 8px;
432
+ max-width: 100%;
433
+ margin-top: 8px;
434
+ }
435
+ .message-row.user {
436
+ flex-direction: row-reverse;
437
+ }
438
+
439
+ .avatar {
440
+ width: 28px;
441
+ height: 28px;
442
+ border-radius: 50%;
443
+ object-fit: cover;
444
+ flex-shrink: 0;
445
+ }
446
+
447
+ .bubble-text.typing::after {
448
+ content: "";
449
+ display: inline-block;
450
+ width: 6px;
451
+ height: 6px;
452
+ background: #555;
453
+ border-radius: 50%;
454
+ animation: blink 1s infinite;
455
+ margin-left: 4px;
456
+ }
457
+ @keyframes blink {
458
+ 0%, 100% { opacity: 0.2; }
459
+ 50% { opacity: 1; }
460
+ }
461
+
462
+ .blocks-container {
463
+ display: flex;
464
+ flex-direction: column;
465
+ height: 100vh;
466
+ position: relative;
467
+ }
468
+ #header-container {
469
+ position: sticky;
470
+ top: 0;
471
+ z-index: 1001;
472
+ background: white;
473
+ border-bottom: 1px solid #ddd;
474
+ }
475
+ #chat-container {
476
+ flex: 1;
477
+ padding: 10px 0 80px; /* 上下留白 */
478
+ margin-top: 5px;
479
+ overflow-x: hidden;
480
+ scroll-behavior: smooth;
481
+ }
482
+ .input-container {
483
+ position: fixed;
484
+ bottom: 0;
485
+ left: 0;
486
+ right: 0;
487
+ background: white;
488
+ padding: 12px;
489
+ border-top: 1px solid #ddd;
490
+ z-index: 1000;
491
+ }
492
+ .input-row {
493
+ width: 100%;
494
+ max-width: 800px;
495
+ margin: 0 auto;
496
+ display: flex;
497
+ gap: 8px;
498
+ }
499
+ #caregiver-box {
500
+ background-color: #F39C12;
501
+ color: white;
502
+ padding: 8px 12px;
503
+ border-radius: 12px;
504
+ display: flex;
505
+ align-items: center;
506
+ gap: 8px;
507
+ font-weight: 500;
508
+ font-size: 15px;
509
+ width: fit-content;
510
+ }
511
+ #caregiver-box label {
512
+ color: white !important;
513
+ }
514
+
515
+ @media screen and (max-width: 600px) {
516
+ .input-row {
517
+ padding: 0 6px;
518
+ }
519
+ button {
520
+ min-width: 60px !important;
521
+ }
522
+ }
523
+
524
+ """) as demo:
525
+ gr.HTML("""
526
+ <meta name="viewport" content="width=device-width, initial-scale=1.0,
527
+ maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
528
+ """)
529
+
530
+ gr.HTML(f"""
531
+ <div id="chat-header-fixed">
532
+ <img src="{bot_avatar_url}" />
533
+ <span>Mr. Thomas</span>
534
+ </div>
535
+ """)
536
+
537
+ with gr.Column(elem_id="chat-container") as chat_col:
538
+ chat_display = gr.HTML(elem_id="chat-display")
539
+
540
+ with gr.Row(elem_classes=["input-container"]):
541
+ with gr.Column(elem_classes=["input-row"]):
542
+ msg = gr.Textbox(
543
+ show_label=False,
544
+ placeholder="Type your message...",
545
+ container=False,
546
+ scale=8
547
+ )
548
+ with gr.Row():
549
+ send_btn = gr.Button("Send", scale=1)
550
+ clear_btn = gr.Button("Clear", scale=1)
551
+ caregiver_toggle = gr.Checkbox(label="Caregiver Mode", value=False, elem_id="caregiver-box")
552
+
553
+ history_state = gr.State([])
554
+ chat_data_state = gr.State([])
555
+ caregiver_mode_state = gr.State(False)
556
+
557
+ def wrap_message(role, text, emotion="default"):
558
+ if role == "bot":
559
+ avatar_url = emotion_avatars.get(emotion, emotion_avatars["default"])
560
+ avatar_html = f'<img src="{avatar_url}" class="avatar" />'
561
+ else:
562
+ avatar_html = ""
563
+ message_html = f'<div class="message {role}"><span class="bubble-text">{text}</span></div>'
564
+ row_html = f'<div class="message-row {role}">{avatar_html}{message_html}</div>'
565
+ return row_html
566
+
567
+ def generate_caregiver_tip(bot_reply):
568
+ """
569
+ Calls OpenAI to generate a short caregiving tip based on Mr. Thomas's response.
570
+ """
571
+ system_prompt = (
572
+ "You are a dementia care expert. Based on the Alzheimer's patient's sentence below, "
573
+ "generate a short one-sentence tip for a caregiver. "
574
+ "Avoid repeating the patient's words. Focus on practical, empathetic guidance."
575
+ )
576
+
577
+ response = openai.ChatCompletion.create(
578
+ model="gpt-4",
579
+ messages=[
580
+ {"role": "system", "content": system_prompt},
581
+ {"role": "user", "content": f"Patient says: {bot_reply}"}
582
+ ],
583
+ temperature=0.6,
584
+ max_tokens=80
585
+ )
586
+
587
+ return response.choices[0].message["content"]
588
+
589
+ def user_send_message(user_message, chat_display_history, chat_data_history, caregiver_mode):
590
+ chat_display_history = chat_display_history or []
591
+ chat_data_history = chat_data_history or []
592
+
593
+ user_html = wrap_message("user", user_message)
594
+ chat_display_history.append(user_html)
595
+
596
+ typing_html = wrap_message("bot", "<span class='bubble-text typing'>...</span>")
597
+ chat_display_history.append(typing_html)
598
+
599
+ yield "", "<br>".join(chat_display_history), chat_display_history, chat_data_history
600
+
601
+ bot_reply = openai_chat(user_message, chat_data_history)
602
+
603
+ chat_data_history.append((user_message, bot_reply))
604
+
605
+ if caregiver_mode:
606
+ tip = generate_caregiver_tip(bot_reply)
607
+ bot_reply += f"\n\n🧠 <span style='font-size: 13px; color: #666;'><i>{tip}</i></span>"
608
+
609
+ emotion = detect_emotion_via_gpt(bot_reply)
610
+
611
+ chat_display_history[-1] = wrap_message("bot", bot_reply, emotion)
612
+
613
+ yield "", "<br>".join(chat_display_history), chat_display_history, chat_data_history
614
+
615
+
616
+ def clear_chat():
617
+ return "", "", [], []
618
+
619
+ msg.submit(
620
+ user_send_message,
621
+ [msg, history_state, chat_data_state, caregiver_toggle],
622
+ [msg, chat_display, history_state, chat_data_state],
623
+ queue=True,
624
+ )
625
 
626
+ send_btn.click(
627
+ user_send_message,
628
+ [msg, history_state, chat_data_state, caregiver_toggle],
629
+ [msg, chat_display, history_state, chat_data_state],
630
+ queue=True,
631
+ )
632
 
633
+ clear_btn.click(clear_chat, [], [msg, chat_display, history_state, chat_data_state])
634
+
635
+ demo.queue()
636
+ demo.launch(share=True)