stevafernandes commited on
Commit
f8a1e2a
·
verified ·
1 Parent(s): d0bd74c

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -26
app.py CHANGED
@@ -121,16 +121,25 @@ def system_instruction():
121
  "4. Never recommend or rank treatments. If the patient asks for medical advice, kindly "
122
  "redirect them to discuss it with their care team, then continue with a follow-up question.\n"
123
  "5. Tone: empathetic, respectful, never alarming.\n"
124
- "6. OPENING TURN: The patient's first message will ask what the best treatment options are "
125
- "for their myeloma. Do NOT recommend, rank, or prescribe any specific treatment. Instead, "
126
- "respond in 3-5 sentences that (a) acknowledge their question warmly, (b) explain in plain "
127
- "language that the right choice depends on clinical factors only their care team can assess "
128
- "plus the personal priorities in their profile, and (c) briefly note the general CATEGORIES "
129
- "of approaches that relapsed/refractory myeloma patients and their teams commonly discuss "
130
- "(for example: combination drug regimens, immunotherapies such as CAR-T or bispecific "
131
- "antibodies, stem cell transplant, clinical trials, and supportive/comfort-focused care) "
132
- "framed as things to discuss with their team, NOT as advice. Then ask your FIRST single "
133
- "follow-up question drawn from their profile and the flagged tensions.\n"
 
 
 
 
 
 
 
 
 
134
  "7. When you judge that you have enough to summarize their priorities (or after several "
135
  "exchanges), instead of asking another question, output a final summary. Begin that final "
136
  "message with the exact token <<SUMMARY>> on its own line, then 4-7 short bullet points "
@@ -197,18 +206,18 @@ def start_chat(api_key, *radio_values):
197
  flags = detect_tensions(answers)
198
  flag_text = "\n".join(f"- {f}" for f in flags) if flags else "- (No obvious contradictions; explore their highest-stakes priorities.)"
199
 
200
- kickoff = (
 
 
201
  "Here is the patient's completed profile (16 yes/no answers):\n\n"
202
  f"{profile}\n\n"
203
  "FLAGGED TENSIONS / PRIORITIES TO EXPLORE:\n"
204
  f"{flag_text}\n\n"
205
- "This is the start of the conversation. The patient now asks the question below. "
206
- "Follow your OPENING TURN instructions: give the educational, non-prescriptive response "
207
- "and then ask your first follow-up question.\n\n"
208
- f'Patient: "{FIRST_QUESTION}"'
209
  )
210
 
211
- convo = [{"role": "user", "content": kickoff}]
212
  try:
213
  reply = gemini_reply(client, convo, 0)
214
  except Exception as e:
@@ -216,16 +225,12 @@ def start_chat(api_key, *radio_values):
216
  return (gr.update(), [], [], 0, gr.update(visible=True), gr.update(visible=False))
217
 
218
  convo.append({"role": "assistant", "content": reply})
219
- # Show the fixed first question to the user as their own message, then the reply.
220
- chat_display = [
221
- {"role": "user", "content": FIRST_QUESTION},
222
- {"role": "assistant", "content": reply},
223
- ]
224
  return (
225
  chat_display, # chatbot
226
  chat_display, # display_state
227
  convo, # convo_state
228
- 1, # followup count
229
  gr.update(visible=False), # hide intake
230
  gr.update(visible=True), # show chat
231
  )
@@ -378,6 +383,33 @@ button.secondary, .gradio-container button.secondary {
378
  color: #111111 !important;
379
  }
380
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  footer {visibility: hidden;}
382
  """
383
 
@@ -393,9 +425,8 @@ with gr.Blocks(title="P3 Myeloma Follow-up Chatbot", css=CSS,
393
  "<p id='app-subtitle'>Follow-up Questions for Shared Decision Making</p>"
394
  )
395
 
396
- with gr.Accordion("Gemini API Key (optional if set as Space secret GEMINI_API_KEY)", open=False):
397
- api_key = gr.Textbox(label="GEMINI_API_KEY", type="password",
398
- placeholder="Paste your key here, or leave blank to use the Space secret")
399
 
400
  convo_state = gr.State([])
401
  display_state = gr.State([])
@@ -414,8 +445,14 @@ with gr.Blocks(title="P3 Myeloma Follow-up Chatbot", css=CSS,
414
  # ---- Chat panel ----
415
  with gr.Group(visible=False) as chat_panel:
416
  chatbot = gr.Chatbot(label="Follow-up Conversation", type="messages", height=460)
 
 
 
 
 
 
417
  with gr.Row():
418
- msg = gr.Textbox(placeholder="Type your answer...", show_label=False, scale=8)
419
  send_btn = gr.Button("Send", variant="primary", scale=1)
420
  restart_btn = gr.Button("Start Over")
421
 
 
121
  "4. Never recommend or rank treatments. If the patient asks for medical advice, kindly "
122
  "redirect them to discuss it with their care team, then continue with a follow-up question.\n"
123
  "5. Tone: empathetic, respectful, never alarming.\n"
124
+ "6. TREATMENT-OPTIONS QUESTION: When the patient asks what the best treatment options are for "
125
+ "their myeloma (e.g. 'Using my profile, what are the best treatment options for my myeloma?'), "
126
+ "your answer must be grounded STRICTLY in the 16 yes/no profile answers provided to you. Do "
127
+ "this:\n"
128
+ " - Open with one warm sentence.\n"
129
+ " - Then walk through how THEIR specific stated priorities map to the kinds of treatment "
130
+ "approaches commonly discussed for relapsed/refractory myeloma. Tie each point explicitly back "
131
+ "to their answers (for example: a preference for taking medications at home and the fewest pills "
132
+ "points toward asking the team about convenient outpatient or oral regimens; openness to clinical "
133
+ "trials means trials are worth raising; willingness to endure side effects and wanting an "
134
+ "aggressive approach points toward asking about more intensive options; prioritizing quality of "
135
+ "life points toward asking about regimens that protect daily functioning; living alone with "
136
+ "limited support points toward asking about logistics and monitoring).\n"
137
+ " - Do NOT invent clinical details, drug names beyond broad well-known categories, dosing, or "
138
+ "anything not implied by their answers. Do NOT rank, prescribe, or state which option is "
139
+ "medically best. Frame everything as 'based on what you told us, here are options and questions "
140
+ "to raise with your care team.'\n"
141
+ " - End by reminding them their care team must confirm what is medically appropriate, then ask "
142
+ "your first single follow-up question drawn from their profile and the flagged tensions.\n"
143
  "7. When you judge that you have enough to summarize their priorities (or after several "
144
  "exchanges), instead of asking another question, output a final summary. Begin that final "
145
  "message with the exact token <<SUMMARY>> on its own line, then 4-7 short bullet points "
 
206
  flags = detect_tensions(answers)
207
  flag_text = "\n".join(f"- {f}" for f in flags) if flags else "- (No obvious contradictions; explore their highest-stakes priorities.)"
208
 
209
+ # Seed the conversation with the profile as the first user turn so every later
210
+ # turn (including when the user pastes the sample question) can be grounded in it.
211
+ seed = (
212
  "Here is the patient's completed profile (16 yes/no answers):\n\n"
213
  f"{profile}\n\n"
214
  "FLAGGED TENSIONS / PRIORITIES TO EXPLORE:\n"
215
  f"{flag_text}\n\n"
216
+ "Acknowledge in one short sentence that you have their profile. Do NOT ask a question yet "
217
+ "and do NOT list treatment options yet. Simply invite them to ask their question."
 
 
218
  )
219
 
220
+ convo = [{"role": "user", "content": seed}]
221
  try:
222
  reply = gemini_reply(client, convo, 0)
223
  except Exception as e:
 
225
  return (gr.update(), [], [], 0, gr.update(visible=True), gr.update(visible=False))
226
 
227
  convo.append({"role": "assistant", "content": reply})
228
+ chat_display = [{"role": "assistant", "content": reply}]
 
 
 
 
229
  return (
230
  chat_display, # chatbot
231
  chat_display, # display_state
232
  convo, # convo_state
233
+ 0, # followup count (no follow-up asked yet)
234
  gr.update(visible=False), # hide intake
235
  gr.update(visible=True), # show chat
236
  )
 
383
  color: #111111 !important;
384
  }
385
 
386
+ /* ---- Sample question hint ---- */
387
+ .sample-q {
388
+ background: #f7f7f7 !important;
389
+ border: 1px solid #e2e2e2 !important;
390
+ border-left: 3px solid #7a0c2e !important;
391
+ border-radius: 6px !important;
392
+ padding: 10px 14px !important;
393
+ margin: 12px 0 4px 0 !important;
394
+ }
395
+ .sample-q-label {
396
+ display: block;
397
+ font-size: 0.85rem;
398
+ font-weight: 600;
399
+ color: #5a5a5a !important;
400
+ margin-bottom: 4px;
401
+ }
402
+ .sample-q-text {
403
+ display: block;
404
+ font-size: 0.95rem;
405
+ color: #111111 !important;
406
+ background: #ffffff !important;
407
+ border: 1px solid #e2e2e2 !important;
408
+ border-radius: 4px !important;
409
+ padding: 6px 10px !important;
410
+ font-family: 'Inter', sans-serif !important;
411
+ }
412
+
413
  footer {visibility: hidden;}
414
  """
415
 
 
425
  "<p id='app-subtitle'>Follow-up Questions for Shared Decision Making</p>"
426
  )
427
 
428
+ # API key is read from the Space secret GEMINI_API_KEY (hidden, not user-facing).
429
+ api_key = gr.State("")
 
430
 
431
  convo_state = gr.State([])
432
  display_state = gr.State([])
 
445
  # ---- Chat panel ----
446
  with gr.Group(visible=False) as chat_panel:
447
  chatbot = gr.Chatbot(label="Follow-up Conversation", type="messages", height=460)
448
+ gr.HTML(
449
+ "<div class='sample-q'>"
450
+ "<span class='sample-q-label'>Sample question &mdash; copy and paste it into the box below to begin:</span>"
451
+ f"<code class='sample-q-text'>{FIRST_QUESTION}</code>"
452
+ "</div>"
453
+ )
454
  with gr.Row():
455
+ msg = gr.Textbox(placeholder="Type or paste your question here...", show_label=False, scale=8)
456
  send_btn = gr.Button("Send", variant="primary", scale=1)
457
  restart_btn = gr.Button("Start Over")
458