XiaoBai1221 commited on
Commit
e9abc9c
·
1 Parent(s): 8860a7b

全面更新系統功能和文檔

Browse files

- 更新 AGENTS.md 文檔說明
- 修復 app.py 中的功能問題
- 優化 services/welcome.py 的歡迎邏輯
- 改進 static/frontend/index.html 的用戶界面

Files changed (4) hide show
  1. AGENTS.md +1 -1
  2. app.py +28 -2
  3. services/welcome.py +50 -7
  4. static/frontend/index.html +2 -1
AGENTS.md CHANGED
@@ -42,7 +42,7 @@
42
  - 對話:前端以 `/ws?token=JWT` 連上;訊息先經「語音綁定 FSM」攔截(若用語音綁定流程)→ 進入 `ChatPipeline` → 視意圖走 MCP 工具或一般聊天 → 落庫 `chats/messages`。
43
  - 檔案分析:`/api/upload-file` 或 `/api/analyze-file-base64`,文字/PDF/圖片分流到對應分析邏輯,底層仍透過 OpenAI。
44
  - 健康資料:建議透過 MCP `healthkit_tool` 查 Firestore(iOS 端直寫 `health_data`)。
45
- - 位置快照:前端成功取得瀏覽器定位時會透過 `env_snapshot` 送到 WebSocket;後端會自動寫入 Firestore 並呼叫 MCP `reverse_geocode` 反查地點,AI Prompt 只會顯示地點名稱(有 label)或地址(無 label),不再硬編碼地標。
46
 
47
 
48
 
 
42
  - 對話:前端以 `/ws?token=JWT` 連上;訊息先經「語音綁定 FSM」攔截(若用語音綁定流程)→ 進入 `ChatPipeline` → 視意圖走 MCP 工具或一般聊天 → 落庫 `chats/messages`。
43
  - 檔案分析:`/api/upload-file` 或 `/api/analyze-file-base64`,文字/PDF/圖片分流到對應分析邏輯,底層仍透過 OpenAI。
44
  - 健康資料:建議透過 MCP `healthkit_tool` 查 Firestore(iOS 端直寫 `health_data`)。
45
+ - 位置快照:前端成功取得瀏覽器定位時會透過 `env_snapshot` 送到 WebSocket;後端會自動寫入 Firestore 並呼叫 MCP `reverse_geocode` 反查地點,AI Prompt 只會顯示地點名稱(有 label)或地址(無 label),不再硬編碼地標。連線歡迎詞也會優先使用最近的 `tz`,避免問候時間錯亂。
46
 
47
 
48
 
app.py CHANGED
@@ -48,6 +48,7 @@ from core.database import (
48
  update_chat_title,
49
  delete_chat,
50
  save_chat_message, # 寫入保持原樣
 
51
  )
52
  # 使用優化版數據庫函數(帶快取)
53
  from core.database.optimized import (
@@ -701,9 +702,22 @@ async def websocket_endpoint_with_jwt(websocket: WebSocket, token: str = Query(N
701
  # 發送個性化歡迎消息(語音登入模式跳過)
702
  if not is_voice_login_mode:
703
  try:
 
 
 
 
 
 
 
 
704
  td = app.state.feature_router.get_current_time_data()
705
  # WebSocket 連線時沒有語音情緒,使用空字串
706
- welcome_msg = compose_welcome(user_name=user_info.get('name'), time_data=td, emotion_label="")
 
 
 
 
 
707
  except Exception as e:
708
  logger.warning(f"生成歡迎訊息失敗: {e}")
709
  welcome_msg = f"歡迎回來,{user_info['name']}!"
@@ -1087,7 +1101,19 @@ async def websocket_endpoint_with_jwt(websocket: WebSocket, token: str = Query(N
1087
  name = user.get("name") or "用戶"
1088
  emo = result.get("emotion") or {}
1089
  emo_label = str(emo.get("label") or "")
1090
- welcome = compose_welcome(user_name=name, time_data=td, emotion_label=emo_label)
 
 
 
 
 
 
 
 
 
 
 
 
1091
  except Exception:
1092
  welcome = None
1093
 
 
48
  update_chat_title,
49
  delete_chat,
50
  save_chat_message, # 寫入保持原樣
51
+ get_user_env_current,
52
  )
53
  # 使用優化版數據庫函數(帶快取)
54
  from core.database.optimized import (
 
702
  # 發送個性化歡迎消息(語音登入模式跳過)
703
  if not is_voice_login_mode:
704
  try:
705
+ tz_hint = None
706
+ try:
707
+ env_res = await get_user_env_current(user_id)
708
+ if env_res.get("success"):
709
+ tz_hint = (env_res.get("context") or {}).get("tz")
710
+ except Exception as tz_err:
711
+ logger.debug(f"讀取使用者時區失敗: {tz_err}")
712
+
713
  td = app.state.feature_router.get_current_time_data()
714
  # WebSocket 連線時沒有語音情緒,使用空字串
715
+ welcome_msg = compose_welcome(
716
+ user_name=user_info.get('name'),
717
+ time_data=td,
718
+ emotion_label="",
719
+ timezone=tz_hint,
720
+ )
721
  except Exception as e:
722
  logger.warning(f"生成歡迎訊息失敗: {e}")
723
  welcome_msg = f"歡迎回來,{user_info['name']}!"
 
1101
  name = user.get("name") or "用戶"
1102
  emo = result.get("emotion") or {}
1103
  emo_label = str(emo.get("label") or "")
1104
+ tz_hint = None
1105
+ try:
1106
+ env_res = await get_user_env_current(user_id)
1107
+ if env_res.get("success"):
1108
+ tz_hint = (env_res.get("context") or {}).get("tz")
1109
+ except Exception as tz_err:
1110
+ logger.debug(f"讀取使用者時區失敗: {tz_err}")
1111
+ welcome = compose_welcome(
1112
+ user_name=name,
1113
+ time_data=td,
1114
+ emotion_label=emo_label,
1115
+ timezone=tz_hint,
1116
+ )
1117
  except Exception:
1118
  welcome = None
1119
 
services/welcome.py CHANGED
@@ -1,6 +1,12 @@
1
  from __future__ import annotations
2
 
3
- from typing import Dict
 
 
 
 
 
 
4
 
5
 
6
  def _greet_from_period(period: str) -> str:
@@ -17,6 +23,24 @@ def _greet_from_period(period: str) -> str:
17
  return mapping.get(period, "您好")
18
 
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  def _mood_from_emotion_label(emo_label: str) -> str:
21
  if not emo_label:
22
  return "很高興再次見到你!"
@@ -35,14 +59,33 @@ def _mood_from_emotion_label(emo_label: str) -> str:
35
  return "很高興再次見到你!"
36
 
37
 
38
- def compose_welcome(user_name: str, time_data: Dict, emotion_label: str) -> str:
 
 
 
 
 
39
  name = user_name or "用戶"
40
- greet = _greet_from_period(str(time_data.get("day_period", "")))
41
- month = time_data.get("month")
42
- day = time_data.get("day")
43
- weekday = time_data.get("weekday_full_chinese", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  date_str = f"{month}月{day}號{weekday}"
45
  mood = _mood_from_emotion_label(str(emotion_label or ""))
46
  return f"{name}{greet}!今天是{date_str},{mood}有什麼要與我分享呢?"
47
 
48
-
 
1
  from __future__ import annotations
2
 
3
+ from datetime import datetime
4
+ from typing import Dict, Optional
5
+
6
+ try:
7
+ from zoneinfo import ZoneInfo # Python 3.9+
8
+ except Exception: # pragma: no cover - zoneinfo may不可用
9
+ ZoneInfo = None # type: ignore
10
 
11
 
12
  def _greet_from_period(period: str) -> str:
 
23
  return mapping.get(period, "您好")
24
 
25
 
26
+ def _derive_period_from_hour(hour: int) -> str:
27
+ if 5 <= hour < 9:
28
+ return "早晨"
29
+ if 9 <= hour < 12:
30
+ return "上午"
31
+ if 12 <= hour < 14:
32
+ return "中午"
33
+ if 14 <= hour < 18:
34
+ return "下午"
35
+ if 18 <= hour < 20:
36
+ return "傍晚"
37
+ if 20 <= hour < 23:
38
+ return "晚上"
39
+ if 23 <= hour or hour < 2:
40
+ return "深夜"
41
+ return "凌晨"
42
+
43
+
44
  def _mood_from_emotion_label(emo_label: str) -> str:
45
  if not emo_label:
46
  return "很高興再次見到你!"
 
59
  return "很高興再次見到你!"
60
 
61
 
62
+ def compose_welcome(
63
+ user_name: str,
64
+ time_data: Dict,
65
+ emotion_label: str,
66
+ timezone: Optional[str] = None,
67
+ ) -> str:
68
  name = user_name or "用戶"
69
+ dt: Optional[datetime] = None
70
+
71
+ if timezone and ZoneInfo:
72
+ try:
73
+ dt = datetime.now(ZoneInfo(timezone))
74
+ except Exception:
75
+ dt = None
76
+
77
+ if dt is None:
78
+ dt = datetime.now()
79
+
80
+ day_period = _derive_period_from_hour(dt.hour)
81
+ greet = _greet_from_period(day_period)
82
+
83
+ month = dt.month
84
+ day = dt.day
85
+ weekday_list = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
86
+ weekday = weekday_list[dt.weekday()]
87
+
88
  date_str = f"{month}月{day}號{weekday}"
89
  mood = _mood_from_emotion_label(str(emotion_label or ""))
90
  return f"{name}{greet}!今天是{date_str},{mood}有什麼要與我分享呢?"
91
 
 
static/frontend/index.html CHANGED
@@ -927,7 +927,8 @@
927
 
928
  /* === Agent 文字輸出區域(打字機效果)=== */
929
  .voice-agent-output {
930
- width: min(100%, 520px);
 
931
  padding: 20px 28px;
932
  background: rgba(255, 255, 255, 0.98);
933
  border: 1px solid rgba(0, 0, 0, 0.1);
 
927
 
928
  /* === Agent 文字輸出區域(打字機效果)=== */
929
  .voice-agent-output {
930
+ width: 100%;
931
+ max-width: 600px;
932
  padding: 20px 28px;
933
  background: rgba(255, 255, 255, 0.98);
934
  border: 1px solid rgba(0, 0, 0, 0.1);