Add feelings selector UI at Stage 2
Browse filesImplements feelings checkboxes similar to needs selector:
- 5 categories of feelings (When Needs Are Met, Sad/Hurt,
Scared/Anxious, Angry/Frustrated, Confused/Tired)
- Appears at Stage 2 (Feeling Reflection)
- Selected feelings stored in session and passed to Claude
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -43,15 +43,34 @@ NEEDS_LIST = {
|
|
| 43 |
]
|
| 44 |
}
|
| 45 |
|
| 46 |
-
|
| 47 |
-
"
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
]
|
| 55 |
|
| 56 |
# ============ SYSTEM PROMPTS ============
|
| 57 |
|
|
@@ -308,7 +327,7 @@ def format_history(history):
|
|
| 308 |
output += "---\n\n"
|
| 309 |
return output
|
| 310 |
|
| 311 |
-
def process_message(user_input, history, stage, session_data, somatic_enabled, selected_needs_str):
|
| 312 |
"""Process user message and return updated state"""
|
| 313 |
|
| 314 |
if not user_input.strip():
|
|
@@ -337,20 +356,26 @@ def process_message(user_input, history, stage, session_data, somatic_enabled, s
|
|
| 337 |
if response is None:
|
| 338 |
error_msg = "**Setup Required:** Please add ANTHROPIC_API_KEY in Space Settings > Repository secrets, then restart."
|
| 339 |
history.append({"user": user_input, "assistant": error_msg})
|
|
|
|
| 340 |
show_needs = (stage == 5)
|
| 341 |
-
return format_history(history), "", history, stage, session_data, f"Stage {stage}: {STAGE_NAMES[stage]}", gr.update(visible=show_needs)
|
| 342 |
|
| 343 |
except Exception as e:
|
| 344 |
error_msg = f"**Error:** {str(e)}"
|
| 345 |
history.append({"user": user_input, "assistant": error_msg})
|
|
|
|
| 346 |
show_needs = (stage == 5)
|
| 347 |
-
return format_history(history), "", history, stage, session_data, f"Stage {stage}: {STAGE_NAMES[stage]}", gr.update(visible=show_needs)
|
| 348 |
|
| 349 |
# Update session data
|
| 350 |
if stage == 1:
|
| 351 |
session_data["original_message"] = user_input
|
| 352 |
elif stage == 2:
|
| 353 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
elif stage == 5 and selected_needs_str:
|
| 355 |
session_data["needs"] = selected_needs_str
|
| 356 |
|
|
@@ -368,7 +393,8 @@ def process_message(user_input, history, stage, session_data, somatic_enabled, s
|
|
| 368 |
next_stage = 1
|
| 369 |
session_data = {}
|
| 370 |
|
| 371 |
-
# Show needs selector
|
|
|
|
| 372 |
show_needs = (next_stage == 5)
|
| 373 |
|
| 374 |
return (
|
|
@@ -378,6 +404,7 @@ def process_message(user_input, history, stage, session_data, somatic_enabled, s
|
|
| 378 |
next_stage,
|
| 379 |
session_data,
|
| 380 |
f"Stage {next_stage}/13: {STAGE_NAMES[next_stage]}",
|
|
|
|
| 381 |
gr.update(visible=show_needs)
|
| 382 |
)
|
| 383 |
|
|
@@ -390,8 +417,11 @@ def reset_session():
|
|
| 390 |
1,
|
| 391 |
{},
|
| 392 |
"Stage 1/13: Share Your Message",
|
| 393 |
-
gr.update(visible=False),
|
| 394 |
-
|
|
|
|
|
|
|
|
|
|
| 395 |
)
|
| 396 |
|
| 397 |
def update_needs_display(e1, e2, e3, e4):
|
|
@@ -399,6 +429,11 @@ def update_needs_display(e1, e2, e3, e4):
|
|
| 399 |
all_needs = e1 + e2 + e3 + e4
|
| 400 |
return ", ".join(all_needs) if all_needs else ""
|
| 401 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
# ============ BUILD INTERFACE ============
|
| 403 |
|
| 404 |
with gr.Blocks(
|
|
@@ -433,6 +468,19 @@ with gr.Blocks(
|
|
| 433 |
label="Conversation"
|
| 434 |
)
|
| 435 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
# Needs selector (hidden until Stage 5)
|
| 437 |
with gr.Accordion("Select Your Needs", open=True, visible=False) as needs_accordion:
|
| 438 |
gr.Markdown("**Select 1-3 needs that resonate most deeply:**")
|
|
@@ -478,6 +526,14 @@ with gr.Blocks(
|
|
| 478 |
[Learn more at CNVC.org](https://www.cnvc.org/)
|
| 479 |
""")
|
| 480 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
# Update needs display
|
| 482 |
for cb in [needs1, needs2, needs3, needs4]:
|
| 483 |
cb.change(
|
|
@@ -489,14 +545,14 @@ with gr.Blocks(
|
|
| 489 |
# Submit handler
|
| 490 |
submit_btn.click(
|
| 491 |
process_message,
|
| 492 |
-
[user_input, history_state, stage_state, session_state, somatic_check, selected_needs],
|
| 493 |
-
[conversation, user_input, history_state, stage_state, session_state, stage_display, needs_accordion]
|
| 494 |
)
|
| 495 |
|
| 496 |
user_input.submit(
|
| 497 |
process_message,
|
| 498 |
-
[user_input, history_state, stage_state, session_state, somatic_check, selected_needs],
|
| 499 |
-
[conversation, user_input, history_state, stage_state, session_state, stage_display, needs_accordion]
|
| 500 |
)
|
| 501 |
|
| 502 |
# Reset handler
|
|
@@ -504,7 +560,8 @@ with gr.Blocks(
|
|
| 504 |
reset_session,
|
| 505 |
[],
|
| 506 |
[conversation, user_input, history_state, stage_state, session_state, stage_display,
|
| 507 |
-
needs_accordion,
|
|
|
|
| 508 |
)
|
| 509 |
|
| 510 |
if __name__ == "__main__":
|
|
|
|
| 43 |
]
|
| 44 |
}
|
| 45 |
|
| 46 |
+
FEELINGS_LIST = {
|
| 47 |
+
"When Needs Are Met": [
|
| 48 |
+
"grateful", "peaceful", "joyful", "content", "hopeful",
|
| 49 |
+
"confident", "curious", "excited", "relieved", "touched",
|
| 50 |
+
"calm", "comfortable", "energized", "fulfilled", "happy",
|
| 51 |
+
"inspired", "loving", "optimistic", "proud", "safe"
|
| 52 |
+
],
|
| 53 |
+
"Sad/Hurt": [
|
| 54 |
+
"sad", "hurt", "disappointed", "heartbroken", "grief",
|
| 55 |
+
"lonely", "hopeless", "dejected", "discouraged", "heavy"
|
| 56 |
+
],
|
| 57 |
+
"Scared/Anxious": [
|
| 58 |
+
"anxious", "scared", "worried", "nervous", "fearful",
|
| 59 |
+
"overwhelmed", "insecure", "vulnerable", "panicked", "dread"
|
| 60 |
+
],
|
| 61 |
+
"Angry/Frustrated": [
|
| 62 |
+
"frustrated", "angry", "annoyed", "irritated", "resentful",
|
| 63 |
+
"furious", "impatient", "exasperated", "agitated", "bitter"
|
| 64 |
+
],
|
| 65 |
+
"Confused/Tired": [
|
| 66 |
+
"confused", "torn", "uncertain", "ambivalent", "puzzled",
|
| 67 |
+
"exhausted", "tired", "depleted", "numb", "disconnected"
|
| 68 |
+
]
|
| 69 |
+
}
|
| 70 |
|
| 71 |
+
FEELINGS_CONNECTED = FEELINGS_LIST["When Needs Are Met"]
|
| 72 |
+
FEELINGS_WANTING = (FEELINGS_LIST["Sad/Hurt"] + FEELINGS_LIST["Scared/Anxious"] +
|
| 73 |
+
FEELINGS_LIST["Angry/Frustrated"] + FEELINGS_LIST["Confused/Tired"])
|
|
|
|
| 74 |
|
| 75 |
# ============ SYSTEM PROMPTS ============
|
| 76 |
|
|
|
|
| 327 |
output += "---\n\n"
|
| 328 |
return output
|
| 329 |
|
| 330 |
+
def process_message(user_input, history, stage, session_data, somatic_enabled, selected_feelings_str, selected_needs_str):
|
| 331 |
"""Process user message and return updated state"""
|
| 332 |
|
| 333 |
if not user_input.strip():
|
|
|
|
| 356 |
if response is None:
|
| 357 |
error_msg = "**Setup Required:** Please add ANTHROPIC_API_KEY in Space Settings > Repository secrets, then restart."
|
| 358 |
history.append({"user": user_input, "assistant": error_msg})
|
| 359 |
+
show_feelings = (stage == 2)
|
| 360 |
show_needs = (stage == 5)
|
| 361 |
+
return format_history(history), "", history, stage, session_data, f"Stage {stage}: {STAGE_NAMES[stage]}", gr.update(visible=show_feelings), gr.update(visible=show_needs)
|
| 362 |
|
| 363 |
except Exception as e:
|
| 364 |
error_msg = f"**Error:** {str(e)}"
|
| 365 |
history.append({"user": user_input, "assistant": error_msg})
|
| 366 |
+
show_feelings = (stage == 2)
|
| 367 |
show_needs = (stage == 5)
|
| 368 |
+
return format_history(history), "", history, stage, session_data, f"Stage {stage}: {STAGE_NAMES[stage]}", gr.update(visible=show_feelings), gr.update(visible=show_needs)
|
| 369 |
|
| 370 |
# Update session data
|
| 371 |
if stage == 1:
|
| 372 |
session_data["original_message"] = user_input
|
| 373 |
elif stage == 2:
|
| 374 |
+
# Use selected feelings from checkboxes if available, otherwise use typed input
|
| 375 |
+
if selected_feelings_str:
|
| 376 |
+
session_data["feelings"] = selected_feelings_str
|
| 377 |
+
else:
|
| 378 |
+
session_data["feelings"] = user_input
|
| 379 |
elif stage == 5 and selected_needs_str:
|
| 380 |
session_data["needs"] = selected_needs_str
|
| 381 |
|
|
|
|
| 393 |
next_stage = 1
|
| 394 |
session_data = {}
|
| 395 |
|
| 396 |
+
# Show feelings selector at stage 2, needs selector at stage 5
|
| 397 |
+
show_feelings = (next_stage == 2)
|
| 398 |
show_needs = (next_stage == 5)
|
| 399 |
|
| 400 |
return (
|
|
|
|
| 404 |
next_stage,
|
| 405 |
session_data,
|
| 406 |
f"Stage {next_stage}/13: {STAGE_NAMES[next_stage]}",
|
| 407 |
+
gr.update(visible=show_feelings),
|
| 408 |
gr.update(visible=show_needs)
|
| 409 |
)
|
| 410 |
|
|
|
|
| 417 |
1,
|
| 418 |
{},
|
| 419 |
"Stage 1/13: Share Your Message",
|
| 420 |
+
gr.update(visible=False), # feelings_accordion
|
| 421 |
+
gr.update(visible=False), # needs_accordion
|
| 422 |
+
[], [], [], [], [], # feelings1-5
|
| 423 |
+
[], # selected_feelings
|
| 424 |
+
[], [], [], [] # needs1-4
|
| 425 |
)
|
| 426 |
|
| 427 |
def update_needs_display(e1, e2, e3, e4):
|
|
|
|
| 429 |
all_needs = e1 + e2 + e3 + e4
|
| 430 |
return ", ".join(all_needs) if all_needs else ""
|
| 431 |
|
| 432 |
+
def update_feelings_display(f1, f2, f3, f4, f5):
|
| 433 |
+
"""Combine selected feelings"""
|
| 434 |
+
all_feelings = f1 + f2 + f3 + f4 + f5
|
| 435 |
+
return ", ".join(all_feelings) if all_feelings else ""
|
| 436 |
+
|
| 437 |
# ============ BUILD INTERFACE ============
|
| 438 |
|
| 439 |
with gr.Blocks(
|
|
|
|
| 468 |
label="Conversation"
|
| 469 |
)
|
| 470 |
|
| 471 |
+
# Feelings selector (hidden until Stage 2)
|
| 472 |
+
with gr.Accordion("Select Your Feelings", open=True, visible=False) as feelings_accordion:
|
| 473 |
+
gr.Markdown("**Select feelings that resonate:**")
|
| 474 |
+
with gr.Row():
|
| 475 |
+
feelings1 = gr.CheckboxGroup(FEELINGS_LIST["When Needs Are Met"], label="When Needs Are Met")
|
| 476 |
+
feelings2 = gr.CheckboxGroup(FEELINGS_LIST["Sad/Hurt"], label="Sad / Hurt")
|
| 477 |
+
with gr.Row():
|
| 478 |
+
feelings3 = gr.CheckboxGroup(FEELINGS_LIST["Scared/Anxious"], label="Scared / Anxious")
|
| 479 |
+
feelings4 = gr.CheckboxGroup(FEELINGS_LIST["Angry/Frustrated"], label="Angry / Frustrated")
|
| 480 |
+
with gr.Row():
|
| 481 |
+
feelings5 = gr.CheckboxGroup(FEELINGS_LIST["Confused/Tired"], label="Confused / Tired")
|
| 482 |
+
selected_feelings = gr.Textbox(label="Your Selected Feelings", interactive=False)
|
| 483 |
+
|
| 484 |
# Needs selector (hidden until Stage 5)
|
| 485 |
with gr.Accordion("Select Your Needs", open=True, visible=False) as needs_accordion:
|
| 486 |
gr.Markdown("**Select 1-3 needs that resonate most deeply:**")
|
|
|
|
| 526 |
[Learn more at CNVC.org](https://www.cnvc.org/)
|
| 527 |
""")
|
| 528 |
|
| 529 |
+
# Update feelings display
|
| 530 |
+
for cb in [feelings1, feelings2, feelings3, feelings4, feelings5]:
|
| 531 |
+
cb.change(
|
| 532 |
+
update_feelings_display,
|
| 533 |
+
[feelings1, feelings2, feelings3, feelings4, feelings5],
|
| 534 |
+
selected_feelings
|
| 535 |
+
)
|
| 536 |
+
|
| 537 |
# Update needs display
|
| 538 |
for cb in [needs1, needs2, needs3, needs4]:
|
| 539 |
cb.change(
|
|
|
|
| 545 |
# Submit handler
|
| 546 |
submit_btn.click(
|
| 547 |
process_message,
|
| 548 |
+
[user_input, history_state, stage_state, session_state, somatic_check, selected_feelings, selected_needs],
|
| 549 |
+
[conversation, user_input, history_state, stage_state, session_state, stage_display, feelings_accordion, needs_accordion]
|
| 550 |
)
|
| 551 |
|
| 552 |
user_input.submit(
|
| 553 |
process_message,
|
| 554 |
+
[user_input, history_state, stage_state, session_state, somatic_check, selected_feelings, selected_needs],
|
| 555 |
+
[conversation, user_input, history_state, stage_state, session_state, stage_display, feelings_accordion, needs_accordion]
|
| 556 |
)
|
| 557 |
|
| 558 |
# Reset handler
|
|
|
|
| 560 |
reset_session,
|
| 561 |
[],
|
| 562 |
[conversation, user_input, history_state, stage_state, session_state, stage_display,
|
| 563 |
+
feelings_accordion, needs_accordion, feelings1, feelings2, feelings3, feelings4, feelings5,
|
| 564 |
+
selected_feelings, needs1, needs2, needs3, needs4]
|
| 565 |
)
|
| 566 |
|
| 567 |
if __name__ == "__main__":
|