Rong6693 commited on
Commit
1fdf5ed
·
verified ·
1 Parent(s): a9f4113

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +234 -276
app.py CHANGED
@@ -1,316 +1,274 @@
1
- import streamlit as st
 
 
2
  import random
3
  from datetime import datetime
4
 
 
 
 
5
  st.set_page_config(
6
  page_title="SoulCompass - Find Your Inner North Star",
7
  page_icon="🧭",
8
  layout="wide"
9
  )
10
 
11
- # 初始化
12
- if 'daily_usage' not in st.session_state:
13
  st.session_state.daily_usage = 0
 
 
14
 
15
- # 簡化塔羅牌
16
- tarot_cards = [
17
- "The Fool - New beginnings and fresh starts",
18
- "The Magician - Power to manifest your dreams",
19
- "The High Priestess - Trust your intuition",
20
- "The Empress - Creativity and abundance",
21
- "The Emperor - Structure and authority",
22
- "The Lovers - Love and important choices",
23
- "The Chariot - Victory through determination",
24
- "Strength - Inner courage and compassion",
25
- "The Hermit - Soul searching and wisdom",
26
- "Wheel of Fortune - Life cycles and destiny",
27
- "Justice - Balance and fairness",
28
- "The Hanged Man - New perspective needed",
29
- "Death - Transformation and renewal",
30
- "Temperance - Patience and moderation",
31
- "The Devil - Breaking free from limitations",
32
- "The Tower - Sudden change and awakening",
33
- "The Star - Hope and inspiration",
34
- "The Moon - Dreams and subconscious",
35
- "The Sun - Joy and success",
36
- "Judgement - Spiritual awakening",
37
- "The World - Completion and achievement"
38
- ]
39
-
40
- # CSS
41
  st.markdown("""
42
  <style>
43
- .header {
44
- text-align: center;
45
- background: linear-gradient(135deg, #6366F1, #C084FC);
46
- color: white;
47
- padding: 3rem;
48
- border-radius: 20px;
49
- margin-bottom: 2rem;
50
- }
51
- .card {
52
- background: linear-gradient(135deg, #1E3A8A, #6366F1);
53
- color: white;
54
- padding: 2rem;
55
- border-radius: 15px;
56
- margin: 1rem;
57
- text-align: center;
58
- border: 2px solid #F59E0B;
59
- }
60
- .result {
61
- background: linear-gradient(135deg, #6366F1, #C084FC);
62
- color: white;
63
- padding: 2rem;
64
- border-radius: 15px;
65
- margin: 2rem 0;
66
- }
67
- .quota {
68
- background: linear-gradient(135deg, #F59E0B, #FB923C);
69
- color: white;
70
- padding: 1rem 2rem;
71
- border-radius: 50px;
72
- text-align: center;
73
- font-weight: bold;
74
- margin: 2rem auto;
75
- max-width: 400px;
76
- }
77
  </style>
78
  """, unsafe_allow_html=True)
79
 
80
- # 主標題
81
  st.markdown("""
82
  <div class="header">
83
- <h1>🧭 SoulCompass</h1>
84
- <h2>Find Your Inner North Star</h2>
85
- <p>🔮 Tarot Reading | 🔢 Numerology | 📖 Soul Journal | 🤖 AI Therapist</p>
86
- <div style="margin-top: 20px;">
87
- <span style="background: rgba(255,255,255,0.2); padding: 8px 16px; border-radius: 20px; margin: 10px;">
88
- 15,247 users
89
- </span>
90
- <span style="background: rgba(255,255,255,0.2); padding: 8px 16px; border-radius: 20px; margin: 10px;">
91
- 💯 4.9/5 rating
92
- </span>
93
- </div>
94
- </div>
95
- """, unsafe_allow_html=True)
96
-
97
- # 剩餘次數
98
- remaining = 5 - st.session_state.daily_usage
99
- st.markdown(f"""
100
- <div class="quota">
101
- 🎫 Daily Free Readings: {remaining}/5
102
  </div>
103
  """, unsafe_allow_html=True)
104
 
105
- # 主要功能區塊
106
- tab1, tab2, tab3, tab4 = st.tabs(["🔮 Tarot Reading", "🔢 Numerology", "📖 Soul Journal", "🤖 AI Therapist"])
107
-
108
- # 🔮 Tarot
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  with tab1:
110
- st.header("🔮 Tarot Reading")
111
  st.write("*Let ancient wisdom guide your path*")
112
 
113
- question = st.text_area(
114
- "What would you like guidance on?",
115
- placeholder="Example: I'm facing a difficult decision at work...",
116
- height=100
117
- )
118
-
119
- if question:
 
120
  spread = st.selectbox(
121
- "Choose your reading type",
122
- ["Single Card - Quick Guidance", "Three Cards - Past Present Future", "Five Cards - Deep Insight"]
123
  )
124
 
125
- if st.button("🔮 Draw Cards", type="primary"):
126
- if st.session_state.daily_usage >= 5:
127
- st.error("😔 Daily limit reached! Come back tomorrow")
128
- else:
129
- st.session_state.daily_usage += 1
130
-
131
- with st.spinner("✨ Drawing your cards..."):
132
- import time
133
- time.sleep(2)
134
-
135
- num_cards = 1 if "Single" in spread else (3 if "Three" in spread else 5)
136
- drawn = random.sample(tarot_cards, num_cards)
137
-
138
- st.success("🌟 Your reading is complete!")
139
-
140
- for i, card in enumerate(drawn):
141
- st.markdown(f"""
142
- <div class="card">
143
- <h3>Card {i+1}</h3>
144
- <p>{card}</p>
145
- </div>
146
- """, unsafe_allow_html=True)
147
-
148
- # 最終解讀
149
- reading = f"""
150
- 🔮 **Your SoulCompass Reading**
151
-
152
- **Question:** {question}
153
-
154
- **Cards Drawn:** {len(drawn)} cards
155
-
156
- **Interpretation:**
157
- The cards reveal important insights about your situation. The first card shows your current energy, while the others provide guidance for moving forward.
158
-
159
- **Key Message:** Trust your inner wisdom and stay open to new possibilities. The universe is supporting your journey.
160
-
161
- **Readings used today:** {st.session_state.daily_usage}/5
162
- """
163
-
164
- st.markdown(f"""
165
- <div class="result">
166
- {reading.replace(chr(10), '<br>')}
167
- </div>
168
- """, unsafe_allow_html=True)
169
-
170
- # 🔢 Numerology
171
  with tab2:
172
- st.header("🔢 Numerology")
173
  st.write("*Discover your numbers*")
174
 
175
  col1, col2 = st.columns(2)
176
-
177
  with col1:
178
- st.subheader("Life Path Number")
179
- birth_date = st.date_input("Your birth date")
180
-
181
- if birth_date and st.button("Calculate"):
182
- date_str = birth_date.strftime('%Y%m%d')
183
- total = sum(int(d) for d in date_str)
184
- while total > 9:
185
- total = sum(int(d) for d in str(total))
186
-
187
- meanings = {
188
- 1: "Leader - Independent and pioneering",
189
- 2: "Peacemaker - Cooperative and diplomatic",
190
- 3: "Creative - Expressive and optimistic",
191
- 4: "Builder - Practical and hardworking",
192
- 5: "Explorer - Freedom-loving and adventurous",
193
- 6: "Nurturer - Caring and responsible",
194
- 7: "Seeker - Analytical and spiritual",
195
- 8: "Achiever - Ambitious and organized",
196
- 9: "Humanitarian - Compassionate and wise"
197
- }
198
-
199
- st.success(f"Your Life Path Number: **{total}**")
200
- st.info(meanings.get(total, "Special number with unique meaning"))
201
 
202
  with col2:
203
- st.subheader("Name Number")
204
- name = st.text_input("Your full name")
205
-
206
- if name and st.button("Analyze Name"):
207
- name_num = len(name) % 9 + 1
208
- st.success(f"Your Name Number: **{name_num}**")
209
- st.info("This number influences how others see you")
 
 
 
210
 
211
- # 📖 Soul Journal
212
  with tab3:
213
- st.header("📖 Soul Journal")
214
- st.write("*Record your inner journey*")
215
-
216
- mood = st.slider("Today's mood (1-10)", 1, 10, 7)
217
- emotion = st.selectbox("Main emotion", ["😊 Happy", "😔 Sad", "😰 Worried", "😌 Calm", "🤗 Grateful"])
218
-
219
- entry = st.text_area(
220
- "Write your thoughts",
221
- placeholder="How are you feeling today? What's on your mind?",
222
- height=120
223
- )
224
-
225
- if st.button("💾 Save Entry"):
226
- if entry:
227
- st.success(" Your journal entry has been saved!")
228
- st.balloons()
229
  else:
230
- st.warning("Please write something first")
231
-
232
- # 🤖 AI Therapist
233
- with tab4:
234
- st.header("🤖 AI Therapist")
235
- st.write("*Your 24/7 companion*")
236
-
237
- st.markdown("""
238
- <div style="background: #F3F4F6; padding: 20px; border-radius: 15px; margin: 20px 0;">
239
- <div style="display: flex; align-items: center;">
240
- <div style="font-size: 3rem; margin-right: 15px;">🤖</div>
241
- <div>
242
- <h4 style="margin: 0; color: #6366F1;">SoulCompass AI Guide</h4>
243
- <p style="margin: 5px 0; color: #6B7280;">Here to listen and support you</p>
244
- </div>
245
- </div>
246
- </div>
247
- """, unsafe_allow_html=True)
248
-
249
- topic = st.selectbox(
250
- "What would you like to talk about?",
251
- ["General Support", "Stress & Anxiety", "Relationships", "Life Goals", "Personal Growth"]
252
- )
253
 
254
- message = st.text_area(
255
- "Share your thoughts",
256
- placeholder="Tell me what's on your mind...",
257
- height=100
258
- )
259
 
260
- if message and st.button("💫 Get Support"):
261
- with st.spinner("🤖 Thinking..."):
262
- import time
263
- time.sleep(1)
264
-
265
- responses = [
266
- f"Thank you for sharing about {topic.lower()}. I hear that you're saying: '{message}'. Your feelings are completely valid, and it's brave of you to reach out.",
267
- f"I understand you're dealing with {topic.lower()}. What you've shared - '{message}' - shows real self-awareness. You're taking positive steps by talking about it.",
268
- f"Regarding {topic.lower()}, I want you to know that '{message}' resonates with me. Remember, you have inner strength and wisdom to navigate this."
269
- ]
270
-
271
- response = random.choice(responses)
272
-
273
- st.markdown(f"""
274
- <div style="margin: 20px 0;">
275
- <div style="background: #E5E7EB; padding: 15px; border-radius: 15px; margin-bottom: 10px;">
276
- <strong>You:</strong> {message}
277
- </div>
278
- <div style="background: #6366F1; color: white; padding: 15px; border-radius: 15px;">
279
- <strong>🤖 AI Guide:</strong> {response}
280
- <br><br>
281
- <small>💙 I'm here to support you. For serious concerns, please reach out to a professional.</small>
282
- </div>
283
- </div>
284
- """, unsafe_allow_html=True)
285
-
286
- # 頁腳
287
- st.markdown("---")
288
- st.markdown("""
289
- <div style="text-align: center; color: #6B7280; padding: 2rem;">
290
- <p>🧭 <strong>SoulCompass</strong> - Find Your Inner North Star</p>
291
- <p>Made with ❤️ for spiritual seekers worldwide</p>
292
- <p>💎 <a href="#" style="color: #6366F1;">Upgrade to Pro</a> | 📧 [email protected] | 🌐 soulcompass.ai</p>
293
- </div>
294
- """, unsafe_allow_html=True)
295
- # app.py 內適當位置(例如新增一個 Tab 或在抽牌後呼叫)
296
- import rag_utils
297
-
298
- with st.expander("🔎 Tarot / Numerology 知識查詢(RAG)"):
299
- q = st.text_input("輸入關鍵字(例如:new beginnings, career change, love advice ...)")
300
- target = st.radio("資料庫", ["Tarot", "Numerology"], horizontal=True)
301
- k = st.slider("Top K", 1, 5, 3)
302
-
303
- if q:
304
- if target == "Tarot":
305
- hits = rag_utils.search_tarot(q, k=k)
306
  else:
307
- hits = rag_utils.search_numerology(q, k=k)
308
-
309
- for h in hits:
310
- st.markdown(f"**#{h['rank']} • score {h['score']:.3f}**")
311
- if 'card_name' in h:
312
- st.markdown(f"🃏 **{h['card_name']}**")
313
- if 'number' in h:
314
- st.markdown(f"🔢 **Number {h['number']}**")
315
- st.write(h['text'])
316
- st.markdown("---")
 
 
 
1
+ # app.py
2
+ import os
3
+ import json
4
  import random
5
  from datetime import datetime
6
 
7
+ import streamlit as st
8
+
9
+ # ========= Page Config =========
10
  st.set_page_config(
11
  page_title="SoulCompass - Find Your Inner North Star",
12
  page_icon="🧭",
13
  layout="wide"
14
  )
15
 
16
+ # ========= Session Init =========
17
+ if "daily_usage" not in st.session_state:
18
  st.session_state.daily_usage = 0
19
+ if "journal" not in st.session_state:
20
+ st.session_state.journal = []
21
 
22
+ # ========= CSS (simple) =========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  st.markdown("""
24
  <style>
25
+ .header {text-align:center;background:linear-gradient(135deg,#6366F1,#C084FC);color:white;padding:2.2rem;border-radius:18px;margin-bottom:1.2rem}
26
+ .badge {display:inline-block;background:rgba(255,255,255,.18);padding:.35rem .75rem;border-radius:999px;margin:.25rem}
27
+ .card {background:linear-gradient(135deg,#1E3A8A,#6366F1);color:white;padding:1.1rem;border-radius:12px;border:2px solid #F59E0B}
28
+ .result {background:linear-gradient(135deg,#6366F1,#C084FC);color:white;padding:1.1rem;border-radius:12px}
29
+ .quota {background:linear-gradient(135deg,#F59E0B,#FB923C);color:white;padding:.5rem 1rem;border-radius:999px;
30
+ text-align:center;font-weight:700;margin:0 auto;max-width:360px}
31
+ .small {font-size:.9rem;color:#6B7280}
32
+ hr{border:none;height:1px;background:#E5E7EB;margin:1.2rem 0}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  </style>
34
  """, unsafe_allow_html=True)
35
 
36
+ # ========= Header =========
37
  st.markdown("""
38
  <div class="header">
39
+ <h1>🧭 SoulCompass</h1>
40
+ <h3>Find Your Inner North Star</h3>
41
+ <div>
42
+ <span class="badge">🔮 Tarot</span>
43
+ <span class="badge">🔢 Numerology</span>
44
+ <span class="badge">📖 Soul Journal</span>
45
+ <span class="badge">🤖 AI Guide</span>
46
+ </div>
 
 
 
 
 
 
 
 
 
 
 
47
  </div>
48
  """, unsafe_allow_html=True)
49
 
50
+ # ========= Sidebar (Model / RAG Controls) =========
51
+ st.sidebar.header("⚙️ Settings")
52
+ model_choice = st.sidebar.selectbox(
53
+ "LLM Model",
54
+ ["cerebras/btlm-3b-8k-base", "mistralai/Mistral-7B-Instruct-v0.2"],
55
+ help="BTLM 輕量穩定、適合免費空間;Mistral 口條更好、但較吃資源。"
56
+ )
57
+ rag_k = st.sidebar.slider("RAG Top-K", 1, 5, 3)
58
+ temperature = st.sidebar.slider("Generation Temperature", 0.2, 1.2, 0.7, 0.1)
59
+
60
+ st.sidebar.markdown("---")
61
+ st.sidebar.caption("資料庫:`data/tarot_data_full.json`, `data/numerology_data_full.json`")
62
+ st.sidebar.caption("檢索程式:`rag_utils.py`")
63
+
64
+ # ========= RAG + LLM Loader =========
65
+ @st.cache_resource
66
+ def load_rag():
67
+ try:
68
+ import rag_utils
69
+ return rag_utils
70
+ except Exception as e:
71
+ st.warning("無法載入 rag_utils,請確認檔案存在於專案根目錄。")
72
+ return None
73
+
74
+ @st.cache_resource
75
+ def load_llm(model_id: str):
76
+ import torch
77
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
78
+ tok = AutoTokenizer.from_pretrained(model_id)
79
+ model = AutoModelForCausalLM.from_pretrained(
80
+ model_id,
81
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
82
+ device_map="auto",
83
+ )
84
+ pipe = pipeline(
85
+ "text-generation",
86
+ model=model,
87
+ tokenizer=tok,
88
+ max_new_tokens=360,
89
+ temperature=temperature,
90
+ top_p=0.9,
91
+ repetition_penalty=1.08,
92
+ do_sample=True,
93
+ )
94
+ return pipe
95
+
96
+ rag = load_rag()
97
+ llm = load_llm(model_choice)
98
+
99
+ def rag_answer(query: str, domain: str = "Tarot", k: int = 3):
100
+ """Search with FAISS and generate an answer using the LLM."""
101
+ if rag is None:
102
+ return "RAG 模組未載入,請檢查 rag_utils.py。", []
103
+ # retrieve
104
+ hits = rag.search_tarot(query, k) if domain == "Tarot" else rag.search_numerology(query, k)
105
+ context = "\n\n".join([h["text"] for h in hits]) if hits else "(無檢索結果)"
106
+ # prompt
107
+ prompt = f"""You are SoulCompass, a compassionate spiritual guide.
108
+ Use ONLY the CONTEXT to answer the USER in Traditional Chinese with empathy and clarity.
109
+ If info is missing, admit briefly and suggest a next step.
110
+
111
+ CONTEXT:
112
+ {context}
113
+
114
+ USER QUESTION: {query}
115
+
116
+ 請用 5~8 句繁體中文回覆,語氣溫柔、務實,最後給出 1 句可行的小建議。"""
117
+ out = llm(prompt)[0]["generated_text"]
118
+ answer = out[len(prompt):].strip()
119
+ return answer, hits
120
+
121
+ # ========= Data Helpers =========
122
+ @st.cache_resource
123
+ def load_tarot_names():
124
+ path = os.path.join("data", "tarot_data_full.json")
125
+ if not os.path.exists(path):
126
+ # tiny fallback
127
+ return ["The Fool", "The Magician", "The High Priestess", "The Empress", "The Emperor"]
128
+ with open(path, "r", encoding="utf-8") as f:
129
+ data = json.load(f)
130
+ return [c["name"] if "name" in c else c.get("card_name", "Card") for c in data]
131
+
132
+ TAROT_NAMES = load_tarot_names()
133
+
134
+ # ========= Tabs =========
135
+ tab1, tab2, tab3, tab4 = st.tabs(["🔮 Tarot", "🔢 Numerology", "📖 Journal", "🤖 AI Guide"])
136
+
137
+ # ---- TAB 1: Tarot ----
138
  with tab1:
139
+ st.subheader("🔮 Tarot Reading")
140
  st.write("*Let ancient wisdom guide your path*")
141
 
142
+ colA, colB = st.columns([2, 1])
143
+ with colA:
144
+ question = st.text_area(
145
+ "你想得到什麼指引?",
146
+ placeholder="例如:我適合換工作嗎?和他未來的關係會如何?",
147
+ height=90
148
+ )
149
+ with colB:
150
  spread = st.selectbox(
151
+ "抽牌陣列",
152
+ ["Single (1 card)", "Three (Past Present Future)", "Five (Deep Insight)"]
153
  )
154
 
155
+ draw = st.button("🔮 抽牌")
156
+ if draw and not question:
157
+ st.warning("請先輸入你的問題")
158
+ if draw and question:
159
+ if st.session_state.daily_usage >= 5:
160
+ st.error("今日免費占卜次數已滿,明天再來吧!")
161
+ else:
162
+ st.session_state.daily_usage += 1
163
+ num_cards = 1 if "Single" in spread else (3 if "Three" in spread else 5)
164
+ drawn = random.sample(TAROT_NAMES, k=min(num_cards, len(TAROT_NAMES)))
165
+
166
+ st.success("🌟 抽牌完成")
167
+ cols = st.columns(len(drawn))
168
+ for i, name in enumerate(drawn):
169
+ with cols[i]:
170
+ st.markdown(f'<div class="card"><h4>Card {i+1}</h4><p>{name}</p></div>', unsafe_allow_html=True)
171
+
172
+ # RAG + LLM 解讀:把「問題 + 牌名列表」一併送去檢索
173
+ query = f"問題:{question};抽到的牌:{', '.join(drawn)}。請用塔羅牌義做整體解讀。"
174
+ with st.spinner("RAG 檢索中 & AI 正在生成解讀…"):
175
+ answer, refs = rag_answer(query, domain="Tarot", k=rag_k)
176
+
177
+ st.markdown(f'<div class="result">{answer.replace(chr(10), "<br>")}</div>', unsafe_allow_html=True)
178
+ st.caption(f"已使用:{st.session_state.daily_usage}/5 次")
179
+
180
+ with st.expander("查看參考段落(RAG 來源)"):
181
+ for i, r in enumerate(refs, 1):
182
+ tag = r.get("card_name") or r.get("name") or "Card"
183
+ st.markdown(f"**#{i} {tag}** · score: {r['score']:.3f}")
184
+ st.write(r["text"])
185
+ st.markdown("---")
186
+
187
+ # ---- TAB 2: Numerology ----
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  with tab2:
189
+ st.subheader("🔢 Numerology")
190
  st.write("*Discover your numbers*")
191
 
192
  col1, col2 = st.columns(2)
 
193
  with col1:
194
+ birth_date = st.date_input("你的生日")
195
+ if st.button("計算生命靈數"):
196
+ if birth_date:
197
+ s = birth_date.strftime("%Y%m%d")
198
+ total = sum(int(d) for d in s)
199
+ while total > 9 and total not in (11, 22, 33):
200
+ total = sum(int(d) for d in str(total))
201
+ st.success(f"你的生命靈數是:{total}")
202
+ with st.spinner("查詢靈數意義…"):
203
+ q = f"生命靈數 {total} 的核心特質與建議是什麼?"
204
+ ans, refs = rag_answer(q, domain="Numerology", k=rag_k)
205
+ st.markdown(f'<div class="result">{ans.replace(chr(10), "<br>")}</div>', unsafe_allow_html=True)
206
+ with st.expander("參考段落"):
207
+ for i, r in enumerate(refs, 1):
208
+ tag = r.get("number") or "N/A"
209
+ st.markdown(f"**#{i} Number {tag}** · score: {r['score']:.3f}")
210
+ st.write(r["text"])
211
+ st.markdown("---")
212
+ else:
213
+ st.warning("請先選擇生日")
 
 
 
214
 
215
  with col2:
216
+ name = st.text_input("你的全名(可選)")
217
+ if st.button("簡易名字數字"):
218
+ if name.strip():
219
+ n = (len(name.replace(" ", "")) % 9) + 1
220
+ st.info(f"名字數字(簡式):{n}")
221
+ q = f"名字數字 {n} 的人在人際與自我表達上的建議?"
222
+ ans, _ = rag_answer(q, domain="Numerology", k=rag_k)
223
+ st.write(ans)
224
+ else:
225
+ st.warning("請輸入名字")
226
 
227
+ # ---- TAB 3: Journal ----
228
  with tab3:
229
+ st.subheader("📖 Soul Journal")
230
+ mood = st.slider("今天的心情(1-10)", 1, 10, 7)
231
+ emotion = st.selectbox("主要情緒", ["😊 開心", "😔 失落", "😰 焦慮", "😌 平靜", "🤗 感恩"])
232
+ entry = st.text_area("寫下今天的想法", height=140,
233
+ placeholder="你最近在思考什麼?今天最感謝的一件事是?")
234
+
235
+ if st.button("💾 儲存日記"):
236
+ if entry.strip():
237
+ st.session_state.journal.append({
238
+ "time": datetime.now().strftime("%Y-%m-%d %H:%M"),
239
+ "mood": mood,
240
+ "emotion": emotion,
241
+ "text": entry.strip()
242
+ })
243
+ st.success("已儲存 🌟")
 
244
  else:
245
+ st.warning("請先寫點內容")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ if st.session_state.journal:
248
+ st.markdown("#### 最近的紀錄")
249
+ for j in reversed(st.session_state.journal[-10:]):
250
+ st.markdown(f"**{j['time']} · {j['emotion']} · 心情 {j['mood']}**")
251
+ st.markdown(f"<div class='card' style='margin:.4rem 0'>{j['text']}</div>", unsafe_allow_html=True)
252
 
253
+ # ---- TAB 4: AI Guide ----
254
+ with tab4:
255
+ st.subheader("🤖 AI Guide")
256
+ st.write("*Your 24/7 companion (not a substitute for professional help)*")
257
+ topic = st.selectbox("主題", ["General Support", "Stress & Anxiety", "Relationships", "Life Goals", "Personal Growth"])
258
+ msg = st.text_area("想說什麼?", height=110, placeholder="把心裡話告訴我…")
259
+ if st.button("💫 取得支持"):
260
+ if not msg.strip():
261
+ st.warning("請先輸入訊息")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  else:
263
+ seed_q = f"主題:{topic};訊息:{msg}。請以溫柔、務實、具行動建議的方式回應。"
264
+ with st.spinner("AI 思考中…"):
265
+ ans, _ = rag_answer(seed_q, domain="Tarot", k=2) # 混搭少量塔羅語料,提升靈性語氣
266
+ st.markdown(f'<div class="result">{ans.replace(chr(10), "<br>")}</div>', unsafe_allow_html=True)
267
+ st.caption("若遇到嚴重困擾,請尋求專業協助。")
268
+
269
+ # ========= Footer =========
270
+ st.markdown("<hr>", unsafe_allow_html=True)
271
+ st.markdown(
272
+ "<div style='text-align:center' class='small'>🧭 <b>SoulCompass</b> · Made with ❤️ · RAG + LLM powered</div>",
273
+ unsafe_allow_html=True
274
+ )