Polish Dream QA sample dropdown and friend grounding
#44
by ADJCJH - opened
- dream_customs/pipeline.py +96 -1
- dream_customs/ui/app.py +31 -60
- dream_customs/ui/copy.py +35 -31
- dream_customs/ui/styles.py +42 -0
- tests/test_pipeline.py +47 -0
- tests/test_ui_actions.py +30 -9
dream_customs/pipeline.py
CHANGED
|
@@ -529,6 +529,22 @@ def _extract_dream_anchors(intake: DreamIntake) -> List[str]:
|
|
| 529 |
candidates.append("frightening dream")
|
| 530 |
if re.search(r"barely slept|many nights|can't sleep|cannot sleep|could not sleep|insomnia|slept", text):
|
| 531 |
candidates.append("lost sleep")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
if re.search(r"\bfloor\s*14\b|\b14\b", text):
|
| 533 |
candidates.append("floor 14")
|
| 534 |
if "button" in text and re.search(r"\b(melt|melted|melting|wax)\b", text):
|
|
@@ -839,6 +855,29 @@ def _contains_any(text: str, terms: List[str]) -> bool:
|
|
| 839 |
|
| 840 |
def _dream_theme(intake: DreamIntake, answers: str = "") -> str:
|
| 841 |
text = _story_text(intake, answers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 842 |
if _contains_any(text, ["小孩", "child", "找不到家", "lost", "home", "回家", "地铁", "subway"]):
|
| 843 |
return "lost_home"
|
| 844 |
if _contains_any(text, ["海", "海浪", "月牙", "moon", "sea", "wave", "dark sea", "dark water", "漆黑的海"]):
|
|
@@ -905,6 +944,7 @@ def _story_anchor_phrase(intake: DreamIntake, anchors: List[str], language: str
|
|
| 905 |
"lost_home": "地铁站里迷路的小孩和回家的方向",
|
| 906 |
"dark_water": "夜晚的海、海浪和月牙下的小人",
|
| 907 |
"library_signal": "旧图书馆、红色楼梯和那张便签",
|
|
|
|
| 908 |
"message_loss": "那条消息、前任和突然消失",
|
| 909 |
"chased": "森林、白色猫和空白路牌",
|
| 910 |
"school_pressure": "教室、作业和来不及交上的感觉",
|
|
@@ -915,6 +955,7 @@ def _story_anchor_phrase(intake: DreamIntake, anchors: List[str], language: str
|
|
| 915 |
"lost_home": "the lost child, the subway, and the way home",
|
| 916 |
"dark_water": "the dark water, the waves, and the small figure under the moon",
|
| 917 |
"library_signal": "the old library, the red staircase, and the note",
|
|
|
|
| 918 |
"message_loss": "the message, the former partner, and the disappearance",
|
| 919 |
"chased": "the chase, the call for help, and the missing route",
|
| 920 |
"school_pressure": "the classroom, the assignment, and the late feeling",
|
|
@@ -1299,6 +1340,14 @@ def _emotion_led_today_tip(intake: DreamIntake, answers: str, anchors: List[str]
|
|
| 1299 |
],
|
| 1300 |
language,
|
| 1301 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1302 |
if theme == "message_loss":
|
| 1303 |
return _numbered_suggestions(
|
| 1304 |
[
|
|
@@ -1347,6 +1396,14 @@ def _emotion_led_today_tip(intake: DreamIntake, answers: str, anchors: List[str]
|
|
| 1347 |
],
|
| 1348 |
language,
|
| 1349 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1350 |
if theme == "message_loss":
|
| 1351 |
return _numbered_suggestions(
|
| 1352 |
[
|
|
@@ -1481,6 +1538,10 @@ def _weird_little_action(intake: DreamIntake, answers: str, anchors: List[str],
|
|
| 1481 |
f"做一张迷你借书卡,书名写「{anchor}」,到期日写“今晚不追讨”;把它夹进一本书里,像把梦暂存进图书馆。",
|
| 1482 |
f"把一张便签折成小楼梯,写上「{anchor}」后让一支笔从第一阶滑到第二阶;今天只允许它上这一阶。",
|
| 1483 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1484 |
"message_loss": [
|
| 1485 |
f"把手机旁边放一张小纸片当“消息降落伞”,写上「{anchor}」的一个词;让纸片从手机边滑下来,今天不真的发送。",
|
| 1486 |
f"画一个迷你信号塔,塔顶写「{anchor}」;把手机倒扣三十秒,像给没发出的消息临时下班。",
|
|
@@ -1517,6 +1578,10 @@ def _weird_little_action(intake: DreamIntake, answers: str, anchors: List[str],
|
|
| 1517 |
f"Make a tiny library card titled {anchor}, set the due date to Not tonight, and tuck it into a book.",
|
| 1518 |
f"Fold a note into a small staircase, write {anchor} on it, and slide a pen from the first step to the second; today it only gets one step.",
|
| 1519 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1520 |
"message_loss": [
|
| 1521 |
f"Put a paper scrap beside your phone as a message parachute, write one word from {anchor}, and let it slide down from the phone without sending anything.",
|
| 1522 |
f"Draw a tiny signal tower with {anchor} at the top, then place your phone face down for thirty seconds so the unsent message can clock out.",
|
|
@@ -1567,6 +1632,14 @@ def _grounded_today_tip(intake: DreamIntake, language: str = "en") -> str:
|
|
| 1567 |
],
|
| 1568 |
language,
|
| 1569 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1570 |
if theme == "message_loss":
|
| 1571 |
return _numbered_suggestions(
|
| 1572 |
[
|
|
@@ -1616,6 +1689,14 @@ def _grounded_today_tip(intake: DreamIntake, language: str = "en") -> str:
|
|
| 1616 |
],
|
| 1617 |
language,
|
| 1618 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1619 |
if theme == "message_loss":
|
| 1620 |
return _numbered_suggestions(
|
| 1621 |
[
|
|
@@ -1926,7 +2007,11 @@ def _text_uses_anchor(text: str, anchors: List[str]) -> bool:
|
|
| 1926 |
continue
|
| 1927 |
if item in clean:
|
| 1928 |
return True
|
| 1929 |
-
tokens = [
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1930 |
if any(token in clean for token in tokens):
|
| 1931 |
return True
|
| 1932 |
return False
|
|
@@ -2244,6 +2329,11 @@ def _decomposition_question(
|
|
| 2244 |
"那张便签或“回家”的感觉,更像想联系某个人,还是想回到一种安定状态?",
|
| 2245 |
"最后只确认今天的需要:你想整理一个现实线索,还是先给自己一点回家的感觉?",
|
| 2246 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2247 |
"message_loss": [
|
| 2248 |
"那条消息最刺痛你的地方,是它出现了、消失了,还是它来自某个具体的人?",
|
| 2249 |
"这个梦更像在问“我还在等什么”,还是在问“我该怎么照顾还没放下的部分”?",
|
|
@@ -2272,6 +2362,11 @@ def _decomposition_question(
|
|
| 2272 |
"Does the stuckness feel more like being late to start, or like trying something and getting no response?",
|
| 2273 |
"Last check before the tip: would one small action help most, or a sentence that lowers the pressure first?",
|
| 2274 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2275 |
}
|
| 2276 |
fallback = [
|
| 2277 |
f"In {story_anchor}, what feeling stayed strongest after waking?",
|
|
|
|
| 529 |
candidates.append("frightening dream")
|
| 530 |
if re.search(r"barely slept|many nights|can't sleep|cannot sleep|could not sleep|insomnia|slept", text):
|
| 531 |
candidates.append("lost sleep")
|
| 532 |
+
if re.search(r"\b(close\s+friend|friend|best\s+friend)\b", text) and re.search(
|
| 533 |
+
r"\b(misunderstood|misunderstand|wronged|hurt|invisible|unseen|unheard|heard|listened|listening)\b",
|
| 534 |
+
text,
|
| 535 |
+
):
|
| 536 |
+
if "close friend" in text:
|
| 537 |
+
candidates.append("close friend")
|
| 538 |
+
else:
|
| 539 |
+
candidates.append("friend")
|
| 540 |
+
if re.search(r"\bmisunderstood|misunderstand\b", text):
|
| 541 |
+
candidates.append("being misunderstood")
|
| 542 |
+
if re.search(r"\bnobody\s+(?:heard|listened)\b|\bno one\s+(?:heard|listened)\b", text):
|
| 543 |
+
candidates.append("nobody heard me")
|
| 544 |
+
if "invisible" in text:
|
| 545 |
+
candidates.append("feeling invisible")
|
| 546 |
+
if "hurt" in text or "wronged" in text:
|
| 547 |
+
candidates.append("feeling hurt")
|
| 548 |
if re.search(r"\bfloor\s*14\b|\b14\b", text):
|
| 549 |
candidates.append("floor 14")
|
| 550 |
if "button" in text and re.search(r"\b(melt|melted|melting|wax)\b", text):
|
|
|
|
| 855 |
|
| 856 |
def _dream_theme(intake: DreamIntake, answers: str = "") -> str:
|
| 857 |
text = _story_text(intake, answers)
|
| 858 |
+
if _contains_any(text, ["friend", "朋友"]) and _contains_any(
|
| 859 |
+
text,
|
| 860 |
+
[
|
| 861 |
+
"misunderstood",
|
| 862 |
+
"misunderstand",
|
| 863 |
+
"nobody heard",
|
| 864 |
+
"nobody listened",
|
| 865 |
+
"no one heard",
|
| 866 |
+
"no one listened",
|
| 867 |
+
"invisible",
|
| 868 |
+
"unseen",
|
| 869 |
+
"unheard",
|
| 870 |
+
"wronged",
|
| 871 |
+
"hurt",
|
| 872 |
+
"误解",
|
| 873 |
+
"没人听",
|
| 874 |
+
"没人听见",
|
| 875 |
+
"看不见",
|
| 876 |
+
"委屈",
|
| 877 |
+
"受伤",
|
| 878 |
+
],
|
| 879 |
+
):
|
| 880 |
+
return "misunderstood_friend"
|
| 881 |
if _contains_any(text, ["小孩", "child", "找不到家", "lost", "home", "回家", "地铁", "subway"]):
|
| 882 |
return "lost_home"
|
| 883 |
if _contains_any(text, ["海", "海浪", "月牙", "moon", "sea", "wave", "dark sea", "dark water", "漆黑的海"]):
|
|
|
|
| 944 |
"lost_home": "地铁站里迷路的小孩和回家的方向",
|
| 945 |
"dark_water": "夜晚的海、海浪和月牙下的小人",
|
| 946 |
"library_signal": "旧图书馆、红色楼梯和那张便签",
|
| 947 |
+
"misunderstood_friend": "朋友的误解、没人听见和受伤感",
|
| 948 |
"message_loss": "那条消息、前任和突然消失",
|
| 949 |
"chased": "森林、白色猫和空白路牌",
|
| 950 |
"school_pressure": "教室、作业和来不及交上的感觉",
|
|
|
|
| 955 |
"lost_home": "the lost child, the subway, and the way home",
|
| 956 |
"dark_water": "the dark water, the waves, and the small figure under the moon",
|
| 957 |
"library_signal": "the old library, the red staircase, and the note",
|
| 958 |
+
"misunderstood_friend": "the close friend, being misunderstood, and feeling invisible",
|
| 959 |
"message_loss": "the message, the former partner, and the disappearance",
|
| 960 |
"chased": "the chase, the call for help, and the missing route",
|
| 961 |
"school_pressure": "the classroom, the assignment, and the late feeling",
|
|
|
|
| 1340 |
],
|
| 1341 |
language,
|
| 1342 |
)
|
| 1343 |
+
if theme == "misunderstood_friend":
|
| 1344 |
+
return _numbered_suggestions(
|
| 1345 |
+
[
|
| 1346 |
+
f"先把「{anchor}」当成醒来后还需要被听见的受伤感,不急着证明谁对谁错。",
|
| 1347 |
+
"今天只做一个温和澄清动作:写下一句“我真正想被听见的是……”,先不急着发送。",
|
| 1348 |
+
],
|
| 1349 |
+
language,
|
| 1350 |
+
)
|
| 1351 |
if theme == "message_loss":
|
| 1352 |
return _numbered_suggestions(
|
| 1353 |
[
|
|
|
|
| 1396 |
],
|
| 1397 |
language,
|
| 1398 |
)
|
| 1399 |
+
if theme == "misunderstood_friend":
|
| 1400 |
+
return _numbered_suggestions(
|
| 1401 |
+
[
|
| 1402 |
+
f"Treat {anchor} as the hurt of not being heard, not as proof that you must fix the whole friendship today.",
|
| 1403 |
+
"Write one gentle clarifying sentence that starts with: What I wanted you to understand was...",
|
| 1404 |
+
],
|
| 1405 |
+
language,
|
| 1406 |
+
)
|
| 1407 |
if theme == "message_loss":
|
| 1408 |
return _numbered_suggestions(
|
| 1409 |
[
|
|
|
|
| 1538 |
f"做一张迷你借书卡,书名写「{anchor}」,到期日写“今晚不追讨”;把它夹进一本书里,像把梦暂存进图书馆。",
|
| 1539 |
f"把一张便签折成小楼梯,写上「{anchor}」后让一支笔从第一阶滑到第二阶;今天只允许它上这一阶。",
|
| 1540 |
],
|
| 1541 |
+
"misunderstood_friend": [
|
| 1542 |
+
f"在便签上写「{anchor}」旁边最想被听见的一句话,把便签对折放十分钟,先不决定要不要发出。",
|
| 1543 |
+
f"画两个小气泡,一个写「{anchor}」,一个写“我真正想说的是”;只填第二个气泡的一句话。",
|
| 1544 |
+
],
|
| 1545 |
"message_loss": [
|
| 1546 |
f"把手机旁边放一张小纸片当“消息降落伞”,写上「{anchor}」的一个词;让纸片从手机边滑下来,今天不真的发送。",
|
| 1547 |
f"画一个迷你信号塔,塔顶写「{anchor}」;把手机倒扣三十秒,像给没发出的消息临时下班。",
|
|
|
|
| 1578 |
f"Make a tiny library card titled {anchor}, set the due date to Not tonight, and tuck it into a book.",
|
| 1579 |
f"Fold a note into a small staircase, write {anchor} on it, and slide a pen from the first step to the second; today it only gets one step.",
|
| 1580 |
],
|
| 1581 |
+
"misunderstood_friend": [
|
| 1582 |
+
f"Write one sentence you wished had been heard beside {anchor}, fold the note once, and wait ten minutes before deciding whether it needs to be sent.",
|
| 1583 |
+
f"Draw two speech bubbles: one labeled {anchor}, one labeled What I meant was. Fill only the second bubble with one gentle sentence.",
|
| 1584 |
+
],
|
| 1585 |
"message_loss": [
|
| 1586 |
f"Put a paper scrap beside your phone as a message parachute, write one word from {anchor}, and let it slide down from the phone without sending anything.",
|
| 1587 |
f"Draw a tiny signal tower with {anchor} at the top, then place your phone face down for thirty seconds so the unsent message can clock out.",
|
|
|
|
| 1632 |
],
|
| 1633 |
language,
|
| 1634 |
)
|
| 1635 |
+
if theme == "misunderstood_friend":
|
| 1636 |
+
return _numbered_suggestions(
|
| 1637 |
+
[
|
| 1638 |
+
f"Use {anchor} to name the part that wanted to be heard.",
|
| 1639 |
+
"Choose one gentle clarification for today: write the sentence privately before deciding whether to say it.",
|
| 1640 |
+
],
|
| 1641 |
+
language,
|
| 1642 |
+
)
|
| 1643 |
if theme == "message_loss":
|
| 1644 |
return _numbered_suggestions(
|
| 1645 |
[
|
|
|
|
| 1689 |
],
|
| 1690 |
language,
|
| 1691 |
)
|
| 1692 |
+
if theme == "misunderstood_friend":
|
| 1693 |
+
return _numbered_suggestions(
|
| 1694 |
+
[
|
| 1695 |
+
f"把「{anchor}」先翻译成“我有一部分想被听见”。",
|
| 1696 |
+
"今天只写一句温和澄清,不急着证明整件事谁对谁错。",
|
| 1697 |
+
],
|
| 1698 |
+
language,
|
| 1699 |
+
)
|
| 1700 |
if theme == "message_loss":
|
| 1701 |
return _numbered_suggestions(
|
| 1702 |
[
|
|
|
|
| 2007 |
continue
|
| 2008 |
if item in clean:
|
| 2009 |
return True
|
| 2010 |
+
tokens = [
|
| 2011 |
+
token
|
| 2012 |
+
for token in re.split(r"[\s,,。::;;、]+", item)
|
| 2013 |
+
if len(token) >= 3 and token not in _ANCHOR_STOPWORDS
|
| 2014 |
+
]
|
| 2015 |
if any(token in clean for token in tokens):
|
| 2016 |
return True
|
| 2017 |
return False
|
|
|
|
| 2329 |
"那张便签或“回家”的感觉,更像想联系某个人,还是想回到一种安定状态?",
|
| 2330 |
"最后只确认今天的需要:你想整理一个现实线索,还是先给自己一点回家的感觉?",
|
| 2331 |
],
|
| 2332 |
+
"misunderstood_friend": [
|
| 2333 |
+
f"在「{story_anchor}」里,最重的是被误解、没人听见,还是醒来后的受伤感?",
|
| 2334 |
+
"如果今天只照顾一个部分,你更想先被理解、先澄清一句话,还是先让自己缓一下?",
|
| 2335 |
+
"最后只确认今天的需要:你想要一句安慰,还是一个很小的沟通动作?",
|
| 2336 |
+
],
|
| 2337 |
"message_loss": [
|
| 2338 |
"那条消息最刺痛你的地方,是它出现了、消失了,还是它来自某个具体的人?",
|
| 2339 |
"这个梦更像在问“我还在等什么”,还是在问“我该怎么照顾还没放下的部分”?",
|
|
|
|
| 2362 |
"Does the stuckness feel more like being late to start, or like trying something and getting no response?",
|
| 2363 |
"Last check before the tip: would one small action help most, or a sentence that lowers the pressure first?",
|
| 2364 |
],
|
| 2365 |
+
"misunderstood_friend": [
|
| 2366 |
+
f"In {story_anchor}, what stayed strongest: being misunderstood, not being heard, or waking up hurt?",
|
| 2367 |
+
"If you cared for only one part today, would you want understanding, one clarifying sentence, or a pause before responding?",
|
| 2368 |
+
"Last check before the tip: would comfort help most, or one small communication step?",
|
| 2369 |
+
],
|
| 2370 |
}
|
| 2371 |
fallback = [
|
| 2372 |
f"In {story_anchor}, what feeling stayed strongest after waking?",
|
dream_customs/ui/app.py
CHANGED
|
@@ -42,9 +42,9 @@ from dream_customs.ui.copy import (
|
|
| 42 |
default_mood_for,
|
| 43 |
mood_options_for,
|
| 44 |
normalize_language,
|
| 45 |
-
EXAMPLE_CHIPS,
|
| 46 |
EXAMPLE_DREAMS,
|
| 47 |
EXAMPLE_MOODS,
|
|
|
|
| 48 |
)
|
| 49 |
from dream_customs.ui.styles import CSS
|
| 50 |
|
|
@@ -888,10 +888,10 @@ def _section_title_html(number: int, text: str) -> str:
|
|
| 888 |
""".strip()
|
| 889 |
|
| 890 |
|
| 891 |
-
def
|
| 892 |
copy = copy_for(language)
|
| 893 |
return f"""
|
| 894 |
-
<div class="dc-
|
| 895 |
<span>{escape(copy['demo_intro_label'])}</span>
|
| 896 |
<strong>{escape(copy['demo_intro_body'])}</strong>
|
| 897 |
</div>
|
|
@@ -992,24 +992,18 @@ def _side_stamp_html(language: str = DEFAULT_LANGUAGE) -> str:
|
|
| 992 |
""".strip()
|
| 993 |
|
| 994 |
|
| 995 |
-
def
|
| 996 |
selected_language = normalize_language(selected_language)
|
| 997 |
-
|
| 998 |
-
if chip:
|
| 999 |
-
return chip
|
| 1000 |
-
return EXAMPLE_DREAMS[selected_language], EXAMPLE_MOODS[selected_language]
|
| 1001 |
|
| 1002 |
|
| 1003 |
-
def
|
| 1004 |
-
|
| 1005 |
-
|
| 1006 |
-
|
| 1007 |
-
|
| 1008 |
-
|
| 1009 |
-
|
| 1010 |
-
|
| 1011 |
-
def _example_melting(selected_language: str):
|
| 1012 |
-
return _example_chip(selected_language, "melting")
|
| 1013 |
|
| 1014 |
|
| 1015 |
def _dev_help_html(language: str = DEFAULT_LANGUAGE) -> str:
|
|
@@ -1075,29 +1069,20 @@ def build_demo() -> gr.Blocks:
|
|
| 1075 |
)
|
| 1076 |
_make_media_api_info_client_safe(audio_input)
|
| 1077 |
field_tip_html = gr.HTML(_field_tip_html(DEFAULT_LANGUAGE))
|
| 1078 |
-
|
| 1079 |
with gr.Row(elem_classes=["dc-submit-row"]):
|
| 1080 |
submit_button = gr.Button(
|
| 1081 |
initial_copy["submit_button"],
|
| 1082 |
variant="primary",
|
| 1083 |
elem_classes=["dc-submit-button"],
|
| 1084 |
)
|
| 1085 |
-
|
| 1086 |
-
|
| 1087 |
-
|
| 1088 |
-
|
| 1089 |
-
|
| 1090 |
-
|
| 1091 |
-
|
| 1092 |
-
initial_copy["example_button_2"],
|
| 1093 |
-
variant="secondary",
|
| 1094 |
-
elem_classes=["dc-demo-chip"],
|
| 1095 |
-
)
|
| 1096 |
-
example_button_3 = gr.Button(
|
| 1097 |
-
initial_copy["example_button_3"],
|
| 1098 |
-
variant="secondary",
|
| 1099 |
-
elem_classes=["dc-demo-chip"],
|
| 1100 |
-
)
|
| 1101 |
processing_html = gr.HTML(_processing_html(DEFAULT_LANGUAGE))
|
| 1102 |
|
| 1103 |
with gr.Group(visible=False, elem_classes=["dc-stage", "dc-question"]) as question_group:
|
|
@@ -1349,23 +1334,9 @@ def build_demo() -> gr.Blocks:
|
|
| 1349 |
show_api=False,
|
| 1350 |
)
|
| 1351 |
|
| 1352 |
-
|
| 1353 |
-
|
| 1354 |
-
inputs=[language],
|
| 1355 |
-
outputs=[dream_text, mood],
|
| 1356 |
-
api_name=False,
|
| 1357 |
-
show_api=False,
|
| 1358 |
-
)
|
| 1359 |
-
example_button_2.click(
|
| 1360 |
-
_example_floor14,
|
| 1361 |
-
inputs=[language],
|
| 1362 |
-
outputs=[dream_text, mood],
|
| 1363 |
-
api_name=False,
|
| 1364 |
-
show_api=False,
|
| 1365 |
-
)
|
| 1366 |
-
example_button_3.click(
|
| 1367 |
-
_example_melting,
|
| 1368 |
-
inputs=[language],
|
| 1369 |
outputs=[dream_text, mood],
|
| 1370 |
api_name=False,
|
| 1371 |
show_api=False,
|
|
@@ -1384,10 +1355,12 @@ def build_demo() -> gr.Blocks:
|
|
| 1384 |
_attachment_html(selected_language),
|
| 1385 |
gr.update(label=copy["image_label"]),
|
| 1386 |
_field_tip_html(selected_language),
|
| 1387 |
-
|
| 1388 |
-
gr.update(
|
| 1389 |
-
|
| 1390 |
-
|
|
|
|
|
|
|
| 1391 |
gr.update(value=copy["submit_button"]),
|
| 1392 |
_processing_html(selected_language),
|
| 1393 |
_question_markdown({"question": ""}, selected_language),
|
|
@@ -1421,10 +1394,8 @@ def build_demo() -> gr.Blocks:
|
|
| 1421 |
attachment_html,
|
| 1422 |
image_input,
|
| 1423 |
field_tip_html,
|
| 1424 |
-
|
| 1425 |
-
|
| 1426 |
-
example_button_2,
|
| 1427 |
-
example_button_3,
|
| 1428 |
submit_button,
|
| 1429 |
processing_html,
|
| 1430 |
question_markdown,
|
|
|
|
| 42 |
default_mood_for,
|
| 43 |
mood_options_for,
|
| 44 |
normalize_language,
|
|
|
|
| 45 |
EXAMPLE_DREAMS,
|
| 46 |
EXAMPLE_MOODS,
|
| 47 |
+
EXAMPLE_SLIPS,
|
| 48 |
)
|
| 49 |
from dream_customs.ui.styles import CSS
|
| 50 |
|
|
|
|
| 888 |
""".strip()
|
| 889 |
|
| 890 |
|
| 891 |
+
def _demo_slip_intro_html(language: str = DEFAULT_LANGUAGE) -> str:
|
| 892 |
copy = copy_for(language)
|
| 893 |
return f"""
|
| 894 |
+
<div class="dc-example-intro">
|
| 895 |
<span>{escape(copy['demo_intro_label'])}</span>
|
| 896 |
<strong>{escape(copy['demo_intro_body'])}</strong>
|
| 897 |
</div>
|
|
|
|
| 992 |
""".strip()
|
| 993 |
|
| 994 |
|
| 995 |
+
def _example_choices(selected_language: str):
|
| 996 |
selected_language = normalize_language(selected_language)
|
| 997 |
+
return [(slip[0], key) for key, slip in EXAMPLE_SLIPS[selected_language].items()]
|
|
|
|
|
|
|
|
|
|
| 998 |
|
| 999 |
|
| 1000 |
+
def _example_slip(selected_language: str, slip_key: str):
|
| 1001 |
+
selected_language = normalize_language(selected_language)
|
| 1002 |
+
slip = EXAMPLE_SLIPS[selected_language].get(slip_key or "")
|
| 1003 |
+
if slip:
|
| 1004 |
+
_label, dream, mood = slip
|
| 1005 |
+
return dream, mood
|
| 1006 |
+
return "", default_mood_for(selected_language)
|
|
|
|
|
|
|
|
|
|
| 1007 |
|
| 1008 |
|
| 1009 |
def _dev_help_html(language: str = DEFAULT_LANGUAGE) -> str:
|
|
|
|
| 1069 |
)
|
| 1070 |
_make_media_api_info_client_safe(audio_input)
|
| 1071 |
field_tip_html = gr.HTML(_field_tip_html(DEFAULT_LANGUAGE))
|
| 1072 |
+
demo_slip_intro = gr.HTML(_demo_slip_intro_html(DEFAULT_LANGUAGE))
|
| 1073 |
with gr.Row(elem_classes=["dc-submit-row"]):
|
| 1074 |
submit_button = gr.Button(
|
| 1075 |
initial_copy["submit_button"],
|
| 1076 |
variant="primary",
|
| 1077 |
elem_classes=["dc-submit-button"],
|
| 1078 |
)
|
| 1079 |
+
example_select = gr.Dropdown(
|
| 1080 |
+
label=initial_copy["example_select_label"],
|
| 1081 |
+
choices=_example_choices(DEFAULT_LANGUAGE),
|
| 1082 |
+
value=None,
|
| 1083 |
+
elem_classes=["dc-example-select"],
|
| 1084 |
+
interactive=True,
|
| 1085 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1086 |
processing_html = gr.HTML(_processing_html(DEFAULT_LANGUAGE))
|
| 1087 |
|
| 1088 |
with gr.Group(visible=False, elem_classes=["dc-stage", "dc-question"]) as question_group:
|
|
|
|
| 1334 |
show_api=False,
|
| 1335 |
)
|
| 1336 |
|
| 1337 |
+
example_select.change(
|
| 1338 |
+
_example_slip,
|
| 1339 |
+
inputs=[language, example_select],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1340 |
outputs=[dream_text, mood],
|
| 1341 |
api_name=False,
|
| 1342 |
show_api=False,
|
|
|
|
| 1355 |
_attachment_html(selected_language),
|
| 1356 |
gr.update(label=copy["image_label"]),
|
| 1357 |
_field_tip_html(selected_language),
|
| 1358 |
+
_demo_slip_intro_html(selected_language),
|
| 1359 |
+
gr.update(
|
| 1360 |
+
label=copy["example_select_label"],
|
| 1361 |
+
choices=_example_choices(selected_language),
|
| 1362 |
+
value=None,
|
| 1363 |
+
),
|
| 1364 |
gr.update(value=copy["submit_button"]),
|
| 1365 |
_processing_html(selected_language),
|
| 1366 |
_question_markdown({"question": ""}, selected_language),
|
|
|
|
| 1394 |
attachment_html,
|
| 1395 |
image_input,
|
| 1396 |
field_tip_html,
|
| 1397 |
+
demo_slip_intro,
|
| 1398 |
+
example_select,
|
|
|
|
|
|
|
| 1399 |
submit_button,
|
| 1400 |
processing_html,
|
| 1401 |
question_markdown,
|
dream_customs/ui/copy.py
CHANGED
|
@@ -16,7 +16,7 @@ APP_COPY = {
|
|
| 16 |
"hero_mobile_note": "Half-awake friendly",
|
| 17 |
"brand_subtitle": "Dream Customs",
|
| 18 |
"steps": ["Record", "Clarify", "Answer", "Tip"],
|
| 19 |
-
"notice_record": "Write one dream fragment,
|
| 20 |
"notice_ask": "Answer this one question, or skip it and turn the existing clues into a Morning Ticket.",
|
| 21 |
"notice_tip": "Your Morning Ticket is ready. Treat it as gentle reflection, not diagnosis or prophecy.",
|
| 22 |
"notice_error": "Dream QA needs a dream fragment before it can continue.",
|
|
@@ -35,9 +35,8 @@ APP_COPY = {
|
|
| 35 |
"voice_label": "Voice note",
|
| 36 |
"voice_help": "Record or upload a short voice note. It is sent to MiMo ASR when you continue.",
|
| 37 |
"field_tip": "The desk looks for three anchors: a place, an object, and the question the dream left behind.",
|
| 38 |
-
"
|
| 39 |
-
"
|
| 40 |
-
"example_button_3": "melting buttons",
|
| 41 |
"submit_button": "Ask one question",
|
| 42 |
"processing_note": (
|
| 43 |
"After submission, the desk extracts dream anchors, asks one grounded question, then writes a Morning Ticket with one Today Tip."
|
|
@@ -66,9 +65,9 @@ APP_COPY = {
|
|
| 66 |
"copy_label": "Copyable result",
|
| 67 |
"side_title": "Waking mood",
|
| 68 |
"mood_label": "Mood",
|
| 69 |
-
"side_stamp_label": "
|
| 70 |
-
"side_stamp_title": "
|
| 71 |
-
"side_stamp_body": "The strongest path
|
| 72 |
"desk_rule_label": "Morning desk rule",
|
| 73 |
"desk_rule_title": "Do not solve the whole dream.",
|
| 74 |
"desk_rule_body": "Catch the strange object, name the feeling, ask one useful question.",
|
|
@@ -92,7 +91,7 @@ APP_COPY = {
|
|
| 92 |
"hero_mobile_note": "适合半醒时使用",
|
| 93 |
"brand_subtitle": "Dream Customs",
|
| 94 |
"steps": ["记录", "明确问题", "回答追问", "Tips"],
|
| 95 |
-
"notice_record": "写一个梦境片段;想走 90 秒演示路径,也可以
|
| 96 |
"notice_ask": "回答这一个追问,或跳过,用已有梦境线索生成清晨小票。",
|
| 97 |
"notice_tip": "清晨小票已生成。把它当作温和参考,不是诊断或预言。",
|
| 98 |
"notice_error": "梦境问答台还没有收到片段。",
|
|
@@ -111,9 +110,8 @@ APP_COPY = {
|
|
| 111 |
"voice_label": "语音片段",
|
| 112 |
"voice_help": "可以录音或上传一小段语音。点击继续后,会直接发送给 MiMo ASR 转写。",
|
| 113 |
"field_tip": "问讯室会优先寻找三个锚点:地点、物件、以及梦醒后留下的问题。",
|
| 114 |
-
"
|
| 115 |
-
"
|
| 116 |
-
"example_button_3": "融化按钮",
|
| 117 |
"submit_button": "问一个问题",
|
| 118 |
"processing_note": (
|
| 119 |
"提交后,问讯室会提取梦境锚点,问一个关键问题,再写成一张清晨小票。"
|
|
@@ -142,9 +140,9 @@ APP_COPY = {
|
|
| 142 |
"copy_label": "可复制结果",
|
| 143 |
"side_title": "醒来后的心情",
|
| 144 |
"mood_label": "心情",
|
| 145 |
-
"side_stamp_label": "
|
| 146 |
-
"side_stamp_title": "
|
| 147 |
-
"side_stamp_body": "最稳的演示
|
| 148 |
"desk_rule_label": "清晨桌面规则",
|
| 149 |
"desk_rule_title": "不要解完整个梦。",
|
| 150 |
"desk_rule_body": "先抓住奇怪的物件,说出醒来的感受,再问一个今天能用的问题。",
|
|
@@ -161,39 +159,45 @@ APP_COPY = {
|
|
| 161 |
}
|
| 162 |
|
| 163 |
EXAMPLE_DREAMS = {
|
| 164 |
-
"en": "I dreamed I
|
| 165 |
-
"zh": "我梦到自己
|
| 166 |
}
|
| 167 |
|
| 168 |
EXAMPLE_MOODS = {"en": "Uneasy", "zh": "焦虑"}
|
| 169 |
|
| 170 |
-
|
| 171 |
"en": {
|
| 172 |
-
"
|
| 173 |
-
"
|
|
|
|
| 174 |
"Uneasy",
|
| 175 |
),
|
| 176 |
-
"
|
| 177 |
-
"
|
|
|
|
| 178 |
"Uneasy",
|
| 179 |
),
|
| 180 |
-
"
|
| 181 |
-
"
|
| 182 |
-
"
|
|
|
|
| 183 |
),
|
| 184 |
},
|
| 185 |
"zh": {
|
| 186 |
-
"
|
| 187 |
-
"
|
|
|
|
| 188 |
"焦虑",
|
| 189 |
),
|
| 190 |
-
"
|
| 191 |
-
"我
|
|
|
|
| 192 |
"焦虑",
|
| 193 |
),
|
| 194 |
-
"
|
| 195 |
-
"
|
| 196 |
-
"
|
|
|
|
| 197 |
),
|
| 198 |
},
|
| 199 |
}
|
|
|
|
| 16 |
"hero_mobile_note": "Half-awake friendly",
|
| 17 |
"brand_subtitle": "Dream Customs",
|
| 18 |
"steps": ["Record", "Clarify", "Answer", "Tip"],
|
| 19 |
+
"notice_record": "Write one dream fragment, or choose a sample dream slip if you want the 90-second judge path.",
|
| 20 |
"notice_ask": "Answer this one question, or skip it and turn the existing clues into a Morning Ticket.",
|
| 21 |
"notice_tip": "Your Morning Ticket is ready. Treat it as gentle reflection, not diagnosis or prophecy.",
|
| 22 |
"notice_error": "Dream QA needs a dream fragment before it can continue.",
|
|
|
|
| 35 |
"voice_label": "Voice note",
|
| 36 |
"voice_help": "Record or upload a short voice note. It is sent to MiMo ASR when you continue.",
|
| 37 |
"field_tip": "The desk looks for three anchors: a place, an object, and the question the dream left behind.",
|
| 38 |
+
"example_select_label": "Sample dream",
|
| 39 |
+
"example_select_placeholder": "Choose a dream slip...",
|
|
|
|
| 40 |
"submit_button": "Ask one question",
|
| 41 |
"processing_note": (
|
| 42 |
"After submission, the desk extracts dream anchors, asks one grounded question, then writes a Morning Ticket with one Today Tip."
|
|
|
|
| 65 |
"copy_label": "Copyable result",
|
| 66 |
"side_title": "Waking mood",
|
| 67 |
"mood_label": "Mood",
|
| 68 |
+
"side_stamp_label": "Quick demo path",
|
| 69 |
+
"side_stamp_title": "Pick a slip, answer or skip",
|
| 70 |
+
"side_stamp_body": "The strongest path shows one grounded question and one Today Tip.",
|
| 71 |
"desk_rule_label": "Morning desk rule",
|
| 72 |
"desk_rule_title": "Do not solve the whole dream.",
|
| 73 |
"desk_rule_body": "Catch the strange object, name the feeling, ask one useful question.",
|
|
|
|
| 91 |
"hero_mobile_note": "适合半醒时使用",
|
| 92 |
"brand_subtitle": "Dream Customs",
|
| 93 |
"steps": ["记录", "明确问题", "回答追问", "Tips"],
|
| 94 |
+
"notice_record": "写一个梦境片段;想走 90 秒演示路径,也可以从示例梦境里选一条。",
|
| 95 |
"notice_ask": "回答这一个追问,或跳过,用已有梦境线索生成清晨小票。",
|
| 96 |
"notice_tip": "清晨小票已生成。把它当作温和参考,不是诊断或预言。",
|
| 97 |
"notice_error": "梦境问答台还没有收到片段。",
|
|
|
|
| 110 |
"voice_label": "语音片段",
|
| 111 |
"voice_help": "可以录音或上传一小段语音。点击继续后,会直接发送给 MiMo ASR 转写。",
|
| 112 |
"field_tip": "问讯室会优先寻找三个锚点:地点、物件、以及梦醒后留下的问题。",
|
| 113 |
+
"example_select_label": "示例梦境",
|
| 114 |
+
"example_select_placeholder": "选择一张梦境纸片...",
|
|
|
|
| 115 |
"submit_button": "问一个问题",
|
| 116 |
"processing_note": (
|
| 117 |
"提交后,问讯室会提取梦境锚点,问一个关键问题,再写成一张清晨小票。"
|
|
|
|
| 140 |
"copy_label": "可复制结果",
|
| 141 |
"side_title": "醒来后的心情",
|
| 142 |
"mood_label": "心情",
|
| 143 |
+
"side_stamp_label": "快速演示路径",
|
| 144 |
+
"side_stamp_title": "选一张纸片,回答或跳过",
|
| 145 |
+
"side_stamp_body": "最稳的演示会展示一个贴着细节的追问和一个今日小 Tips。",
|
| 146 |
"desk_rule_label": "清晨桌面规则",
|
| 147 |
"desk_rule_title": "不要解完整个梦。",
|
| 148 |
"desk_rule_body": "先抓住奇怪的物件,说出醒来的感受,再问一个今天能用的问题。",
|
|
|
|
| 159 |
}
|
| 160 |
|
| 161 |
EXAMPLE_DREAMS = {
|
| 162 |
+
"en": "I dreamed I was late, my phone battery died, and the elevator never arrived. I wanted a quick tip and felt stuck at the entrance.",
|
| 163 |
+
"zh": "我梦到自己快迟到了,手机没电,电梯一直没���。我想要一个很快能用的小建议,但梦里像被卡在入口。",
|
| 164 |
}
|
| 165 |
|
| 166 |
EXAMPLE_MOODS = {"en": "Uneasy", "zh": "焦虑"}
|
| 167 |
|
| 168 |
+
EXAMPLE_SLIPS = {
|
| 169 |
"en": {
|
| 170 |
+
"late_elevator": (
|
| 171 |
+
"Late elevator and dead phone",
|
| 172 |
+
"I dreamed I was late, my phone battery died, and the elevator never arrived. I wanted a quick tip and felt stuck at the entrance.",
|
| 173 |
"Uneasy",
|
| 174 |
),
|
| 175 |
+
"friend_misread": (
|
| 176 |
+
"A friend misunderstood me",
|
| 177 |
+
"I dreamed I sent a message to a friend, but every reply bubble turned into fog. I woke up worried I had been misunderstood.",
|
| 178 |
"Uneasy",
|
| 179 |
),
|
| 180 |
+
"sleepless_loop": (
|
| 181 |
+
"The same sleepless room",
|
| 182 |
+
"I dreamed I kept waking up inside the same room, checking the clock each time, but morning never arrived.",
|
| 183 |
+
"Tired",
|
| 184 |
),
|
| 185 |
},
|
| 186 |
"zh": {
|
| 187 |
+
"late_elevator": (
|
| 188 |
+
"迟到的电梯和没电手机",
|
| 189 |
+
"我梦到自己快迟到了,手机没电,电梯一直没来。我想要一个很快能用的小建议,但梦里像被卡在入口。",
|
| 190 |
"焦虑",
|
| 191 |
),
|
| 192 |
+
"friend_misread": (
|
| 193 |
+
"朋友误解了我",
|
| 194 |
+
"我梦到自己给朋友发消息,但每个回复气泡都变成雾。醒来后担心自己是不是被误解了。",
|
| 195 |
"焦虑",
|
| 196 |
),
|
| 197 |
+
"sleepless_loop": (
|
| 198 |
+
"反复醒来的房间",
|
| 199 |
+
"我梦到自己一次次在同一个房间醒来,每次都看钟,可早晨一直没有到。",
|
| 200 |
+
"疲惫",
|
| 201 |
),
|
| 202 |
},
|
| 203 |
}
|
dream_customs/ui/styles.py
CHANGED
|
@@ -1210,6 +1210,48 @@ a.built-with[href*="gradio.app"] {
|
|
| 1210 |
"""
|
| 1211 |
|
| 1212 |
CSS += """
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1213 |
.dc-question-original p {
|
| 1214 |
font-size: clamp(1.16rem, 2.4vw, 1.45rem);
|
| 1215 |
font-weight: 720;
|
|
|
|
| 1210 |
"""
|
| 1211 |
|
| 1212 |
CSS += """
|
| 1213 |
+
.dc-example-intro {
|
| 1214 |
+
align-items: baseline;
|
| 1215 |
+
color: var(--dqa-muted);
|
| 1216 |
+
display: flex;
|
| 1217 |
+
flex-wrap: wrap;
|
| 1218 |
+
gap: 8px;
|
| 1219 |
+
margin: 18px 0 0;
|
| 1220 |
+
}
|
| 1221 |
+
|
| 1222 |
+
.dc-example-intro span {
|
| 1223 |
+
color: var(--dqa-sage-deep);
|
| 1224 |
+
font-size: 0.78rem;
|
| 1225 |
+
font-weight: 820;
|
| 1226 |
+
text-transform: uppercase;
|
| 1227 |
+
}
|
| 1228 |
+
|
| 1229 |
+
.dc-example-intro strong {
|
| 1230 |
+
color: #59675f;
|
| 1231 |
+
font-size: 0.92rem;
|
| 1232 |
+
font-weight: 650;
|
| 1233 |
+
}
|
| 1234 |
+
|
| 1235 |
+
.dc-example-select {
|
| 1236 |
+
min-width: 0 !important;
|
| 1237 |
+
}
|
| 1238 |
+
|
| 1239 |
+
.dc-example-select .wrap,
|
| 1240 |
+
.dc-example-select .container,
|
| 1241 |
+
.dc-example-select input[role="listbox"] {
|
| 1242 |
+
background: #fff !important;
|
| 1243 |
+
border-color: rgba(79, 138, 88, 0.26) !important;
|
| 1244 |
+
border-radius: 12px !important;
|
| 1245 |
+
color: var(--dqa-ink) !important;
|
| 1246 |
+
min-height: 58px !important;
|
| 1247 |
+
}
|
| 1248 |
+
|
| 1249 |
+
.dc-example-select label,
|
| 1250 |
+
.dc-example-select [data-testid="block-label"] {
|
| 1251 |
+
color: var(--dqa-sage-deep) !important;
|
| 1252 |
+
font-size: 0.82rem !important;
|
| 1253 |
+
}
|
| 1254 |
+
|
| 1255 |
.dc-question-original p {
|
| 1256 |
font-size: clamp(1.16rem, 2.4vw, 1.45rem);
|
| 1257 |
font-weight: 720;
|
tests/test_pipeline.py
CHANGED
|
@@ -13,6 +13,7 @@ from dream_customs.pipeline import (
|
|
| 13 |
create_session,
|
| 14 |
dated_permit_id,
|
| 15 |
draft_pact,
|
|
|
|
| 16 |
generate_negotiation,
|
| 17 |
generate_pact,
|
| 18 |
generate_today_tip,
|
|
@@ -669,6 +670,52 @@ def test_today_tip_keeps_friend_misunderstanding_emotional_instead_of_email_draf
|
|
| 669 |
assert not re.search(r"\b[123]\.", card.today_tip)
|
| 670 |
|
| 671 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
def test_today_tip_skip_path_uses_original_anchors_without_default_email_or_broken_caring_note():
|
| 673 |
class PollutedSkipClient:
|
| 674 |
def generate_today_tip(self, prompt):
|
|
|
|
| 13 |
create_session,
|
| 14 |
dated_permit_id,
|
| 15 |
draft_pact,
|
| 16 |
+
finish_today_tip,
|
| 17 |
generate_negotiation,
|
| 18 |
generate_pact,
|
| 19 |
generate_today_tip,
|
|
|
|
| 670 |
assert not re.search(r"\b[123]\.", card.today_tip)
|
| 671 |
|
| 672 |
|
| 673 |
+
def test_friend_misunderstanding_skip_path_keeps_relationship_emotion_anchors():
|
| 674 |
+
dream_text = (
|
| 675 |
+
"I dreamed my close friend misunderstood me. I kept explaining, but nobody heard me, "
|
| 676 |
+
"and I woke up feeling hurt and invisible."
|
| 677 |
+
)
|
| 678 |
+
session = add_evidence(
|
| 679 |
+
create_session(),
|
| 680 |
+
dream_text=dream_text,
|
| 681 |
+
mood="Hurt and invisible",
|
| 682 |
+
vision_client=FakeVisionClient(),
|
| 683 |
+
asr_client=FakeASRClient(),
|
| 684 |
+
language="en",
|
| 685 |
+
)
|
| 686 |
+
|
| 687 |
+
assert "close friend" in session.qa_state.dream_anchors
|
| 688 |
+
assert "being misunderstood" in session.qa_state.dream_anchors
|
| 689 |
+
assert "nobody heard me" in session.qa_state.dream_anchors
|
| 690 |
+
assert "feeling invisible" in session.qa_state.dream_anchors
|
| 691 |
+
assert "feeling hurt" in session.qa_state.dream_anchors
|
| 692 |
+
|
| 693 |
+
session = ask_questions(session, FakeTextClient(), language="en")
|
| 694 |
+
question_text = session.question_history[0].lower()
|
| 695 |
+
|
| 696 |
+
assert "friend" in question_text
|
| 697 |
+
assert "misunderstood" in question_text or "heard" in question_text or "hurt" in question_text
|
| 698 |
+
assert "former partner" not in question_text
|
| 699 |
+
assert "disappearance" not in question_text
|
| 700 |
+
assert "dream clues" not in question_text
|
| 701 |
+
|
| 702 |
+
session = skip_question(session, language="en")
|
| 703 |
+
session = finish_today_tip(session, FakeTextClient(), language="en")
|
| 704 |
+
card = session.sealed_tip
|
| 705 |
+
combined = _today_tip_public_text(card).lower()
|
| 706 |
+
|
| 707 |
+
assert "friend" in combined
|
| 708 |
+
assert "misunderstood" in combined or "heard" in combined
|
| 709 |
+
assert "invisible" in combined or "hurt" in combined
|
| 710 |
+
assert "former partner" not in combined
|
| 711 |
+
assert "disappearance" not in combined
|
| 712 |
+
assert "dream clues" not in combined
|
| 713 |
+
assert "stuck before starting" not in combined
|
| 714 |
+
assert "overdue email" not in combined
|
| 715 |
+
assert "first sentence of the email" not in combined
|
| 716 |
+
assert not re.search(r"\b[123]\.", card.today_tip)
|
| 717 |
+
|
| 718 |
+
|
| 719 |
def test_today_tip_skip_path_uses_original_anchors_without_default_email_or_broken_caring_note():
|
| 720 |
class PollutedSkipClient:
|
| 721 |
def generate_today_tip(self, prompt):
|
tests/test_ui_actions.py
CHANGED
|
@@ -134,17 +134,33 @@ def test_hero_subtitle_explains_app_instead_of_legacy_name():
|
|
| 134 |
assert "Dream Customs" not in hero_html
|
| 135 |
|
| 136 |
|
| 137 |
-
def
|
| 138 |
source = inspect.getsource(ui_app.build_demo)
|
| 139 |
|
| 140 |
-
assert 'elem_classes=["dc-
|
| 141 |
-
assert
|
| 142 |
-
assert "
|
| 143 |
-
assert "
|
| 144 |
-
assert "
|
| 145 |
-
assert
|
| 146 |
-
assert "
|
| 147 |
-
assert "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
|
| 150 |
def test_side_panel_frames_the_morning_desk_rule():
|
|
@@ -158,8 +174,13 @@ def test_side_panel_frames_the_morning_desk_rule():
|
|
| 158 |
assert "Text" in html
|
| 159 |
assert "Image" in html
|
| 160 |
assert "Voice" in html
|
|
|
|
|
|
|
|
|
|
| 161 |
assert "清晨桌面规则" in zh_html
|
| 162 |
assert "文字" in zh_html
|
|
|
|
|
|
|
| 163 |
|
| 164 |
|
| 165 |
def test_side_panel_dropdown_hover_stays_readable():
|
|
|
|
| 134 |
assert "Dream Customs" not in hero_html
|
| 135 |
|
| 136 |
|
| 137 |
+
def test_composer_uses_one_sample_dream_dropdown():
|
| 138 |
source = inspect.getsource(ui_app.build_demo)
|
| 139 |
|
| 140 |
+
assert 'elem_classes=["dc-example-select"]' in source
|
| 141 |
+
assert "example_select = gr.Dropdown(" in source
|
| 142 |
+
assert "_example_slip" in source
|
| 143 |
+
assert "_example_elevator" not in source
|
| 144 |
+
assert 'elem_classes=["dc-demo-chip-row"]' not in source
|
| 145 |
+
assert source.count('elem_classes=["dc-demo-chip"]') == 0
|
| 146 |
+
assert ".dc-example-select" in CSS
|
| 147 |
+
assert "Dream slips" in ui_app._demo_slip_intro_html("en")
|
| 148 |
+
assert "梦境纸片" in ui_app._demo_slip_intro_html("zh")
|
| 149 |
+
|
| 150 |
+
choices = ui_app._example_choices("en")
|
| 151 |
+
labels = [label for label, _key in choices]
|
| 152 |
+
assert labels == [
|
| 153 |
+
"Late elevator and dead phone",
|
| 154 |
+
"A friend misunderstood me",
|
| 155 |
+
"The same sleepless room",
|
| 156 |
+
]
|
| 157 |
+
dream_text, mood = ui_app._example_slip("en", "friend_misread")
|
| 158 |
+
assert "reply bubble" in dream_text
|
| 159 |
+
assert mood == "Uneasy"
|
| 160 |
+
|
| 161 |
+
zh_text, zh_mood = ui_app._example_slip("zh", "sleepless_loop")
|
| 162 |
+
assert "同一个房间" in zh_text
|
| 163 |
+
assert zh_mood == "疲惫"
|
| 164 |
|
| 165 |
|
| 166 |
def test_side_panel_frames_the_morning_desk_rule():
|
|
|
|
| 174 |
assert "Text" in html
|
| 175 |
assert "Image" in html
|
| 176 |
assert "Voice" in html
|
| 177 |
+
assert "first sentence" not in html.lower()
|
| 178 |
+
assert "overdue email" not in html.lower()
|
| 179 |
+
assert "Pick a slip" in html
|
| 180 |
assert "清晨桌面规则" in zh_html
|
| 181 |
assert "文字" in zh_html
|
| 182 |
+
assert "第一句话" not in zh_html
|
| 183 |
+
assert "邮件" not in zh_html
|
| 184 |
|
| 185 |
|
| 186 |
def test_side_panel_dropdown_hover_stays_readable():
|