Spaces:
Runtime error
Runtime error
Upload 2 files
Browse files- main_app.py +154 -29
- persistent_user_manager.py +15 -3
main_app.py
CHANGED
|
@@ -482,17 +482,28 @@ def initialize_session_state(managers, force_reset_override=False):
|
|
| 482 |
user_id_manager = managers["user_id_manager"] # フォールバック用
|
| 483 |
session_api_client = managers["session_api_client"]
|
| 484 |
|
| 485 |
-
# セッションID
|
| 486 |
if 'user_id' not in st.session_state or force_reset:
|
|
|
|
|
|
|
|
|
|
| 487 |
try:
|
| 488 |
# 永続ストレージからCookieベースでユーザーIDを取得
|
| 489 |
session_id = persistent_user_manager.get_or_create_user_id()
|
| 490 |
logger.info(f"永続ストレージからユーザーIDを取得: {session_id[:8]}...")
|
| 491 |
except Exception as e:
|
| 492 |
-
logger.
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
else:
|
| 497 |
session_id = st.session_state.user_id
|
| 498 |
logger.debug(f"既存ユーザーID使用: {session_id[:8]}...")
|
|
@@ -534,18 +545,29 @@ def initialize_session_state(managers, force_reset_override=False):
|
|
| 534 |
st.session_state.chat_initialized = False
|
| 535 |
|
| 536 |
if not st.session_state.chat_initialized:
|
| 537 |
-
#
|
| 538 |
saved_game_data = None
|
|
|
|
|
|
|
| 539 |
try:
|
| 540 |
saved_game_data = persistent_user_manager.load_user_game_data(st.session_state.user_id)
|
| 541 |
if saved_game_data:
|
| 542 |
logger.info(f"永続ストレージからゲームデータを読み込み: {st.session_state.user_id[:8]}...")
|
|
|
|
|
|
|
| 543 |
except Exception as e:
|
| 544 |
-
logger.
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
|
| 550 |
if saved_game_data and not force_reset:
|
| 551 |
# 保存データから復元
|
|
@@ -2219,24 +2241,7 @@ Streamlit情報:
|
|
| 2219 |
st.session_state.chat['messages'] = []
|
| 2220 |
if "affection_notifications" not in st.session_state:
|
| 2221 |
st.session_state.affection_notifications = []
|
| 2222 |
-
|
| 2223 |
-
user_input = st.chat_input("麻理に話しかける...")
|
| 2224 |
-
if user_input and not st.session_state.awaiting_response:
|
| 2225 |
-
st.session_state.latest_prompt = user_input
|
| 2226 |
-
st.session_state.awaiting_response = True
|
| 2227 |
-
|
| 2228 |
-
# タブ増殖防止:rerun前にチャット再レンダリングフラグを設定
|
| 2229 |
-
st.session_state.force_chat_rerender = True
|
| 2230 |
-
|
| 2231 |
-
# rerun回数を制限(無限ループ防止)
|
| 2232 |
-
rerun_count = st.session_state.get('rerun_count', 0)
|
| 2233 |
-
if rerun_count < 10: # 最大10回まで
|
| 2234 |
-
st.session_state.rerun_count = rerun_count + 1
|
| 2235 |
-
logger.info(f"メッセージ送信によるrerun実行 (回数: {st.session_state.rerun_count})")
|
| 2236 |
-
st.rerun()
|
| 2237 |
-
else:
|
| 2238 |
-
logger.warning("rerun回数制限に達しました。処理を継続します。")
|
| 2239 |
-
st.session_state.rerun_count = 0
|
| 2240 |
|
| 2241 |
# 応答処理(rerun 後にこのブロックが実行される)
|
| 2242 |
if st.session_state.awaiting_response:
|
|
@@ -3182,6 +3187,126 @@ def main():
|
|
| 3182 |
|
| 3183 |
tutorial_manager.render_tutorial_tab()
|
| 3184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3185 |
if __name__ == "__main__":
|
| 3186 |
if not Config.validate_config():
|
| 3187 |
logger.critical("手紙機能の設定にエラーがあります。アプリケーションを起動できません。")
|
|
|
|
| 482 |
user_id_manager = managers["user_id_manager"] # フォールバック用
|
| 483 |
session_api_client = managers["session_api_client"]
|
| 484 |
|
| 485 |
+
# セッションIDを取得または生成(重複防止・統一化)
|
| 486 |
if 'user_id' not in st.session_state or force_reset:
|
| 487 |
+
# 永続ストレージを優先、エラー時のみフォールバック
|
| 488 |
+
session_id = None
|
| 489 |
+
|
| 490 |
try:
|
| 491 |
# 永続ストレージからCookieベースでユーザーIDを取得
|
| 492 |
session_id = persistent_user_manager.get_or_create_user_id()
|
| 493 |
logger.info(f"永続ストレージからユーザーIDを取得: {session_id[:8]}...")
|
| 494 |
except Exception as e:
|
| 495 |
+
logger.warning(f"永続ストレージ取得エラー、フォールバック使用: {e}")
|
| 496 |
+
|
| 497 |
+
try:
|
| 498 |
+
# フォールバック: 従来のローカルファイル方式
|
| 499 |
+
session_id = user_id_manager.get_or_create_user_id()
|
| 500 |
+
logger.info(f"フォールバック: ローカルファイルからユーザーIDを取得: {session_id[:8]}...")
|
| 501 |
+
except Exception as e2:
|
| 502 |
+
logger.error(f"フォールバックも失敗: {e2}")
|
| 503 |
+
# 最終フォールバック: 一時的なIDを生成
|
| 504 |
+
import uuid
|
| 505 |
+
session_id = str(uuid.uuid4())
|
| 506 |
+
logger.warning(f"最終フォールバック: 一時的なIDを生成: {session_id[:8]}...")
|
| 507 |
else:
|
| 508 |
session_id = st.session_state.user_id
|
| 509 |
logger.debug(f"既存ユーザーID使用: {session_id[:8]}...")
|
|
|
|
| 545 |
st.session_state.chat_initialized = False
|
| 546 |
|
| 547 |
if not st.session_state.chat_initialized:
|
| 548 |
+
# 保存されたゲームデータを読み込み(重複防止・統一化)
|
| 549 |
saved_game_data = None
|
| 550 |
+
|
| 551 |
+
# 永続ストレージを優先、失敗時のみフォールバック
|
| 552 |
try:
|
| 553 |
saved_game_data = persistent_user_manager.load_user_game_data(st.session_state.user_id)
|
| 554 |
if saved_game_data:
|
| 555 |
logger.info(f"永続ストレージからゲームデータを読み込み: {st.session_state.user_id[:8]}...")
|
| 556 |
+
else:
|
| 557 |
+
logger.debug("永続ストレージにゲームデータなし")
|
| 558 |
except Exception as e:
|
| 559 |
+
logger.warning(f"永続ストレージ読み込みエラー、フォールバック試行: {e}")
|
| 560 |
+
|
| 561 |
+
try:
|
| 562 |
+
# フォールバック: 従来のローカルファイル方式
|
| 563 |
+
saved_game_data = user_id_manager.load_game_data(st.session_state.user_id)
|
| 564 |
+
if saved_game_data:
|
| 565 |
+
logger.info(f"フォールバック: ローカルファイルからゲームデータを読み込み")
|
| 566 |
+
else:
|
| 567 |
+
logger.debug("フォールバック: ローカルファイルにもゲームデータなし")
|
| 568 |
+
except Exception as e2:
|
| 569 |
+
logger.error(f"フォールバック読み込みも失敗: {e2}")
|
| 570 |
+
saved_game_data = None
|
| 571 |
|
| 572 |
if saved_game_data and not force_reset:
|
| 573 |
# 保存データから復元
|
|
|
|
| 2241 |
st.session_state.chat['messages'] = []
|
| 2242 |
if "affection_notifications" not in st.session_state:
|
| 2243 |
st.session_state.affection_notifications = []
|
| 2244 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2245 |
|
| 2246 |
# 応答処理(rerun 後にこのブロックが実行される)
|
| 2247 |
if st.session_state.awaiting_response:
|
|
|
|
| 3187 |
|
| 3188 |
tutorial_manager.render_tutorial_tab()
|
| 3189 |
|
| 3190 |
+
def main():
|
| 3191 |
+
"""メインアプリケーション関数"""
|
| 3192 |
+
# 初期化の二重実行を防ぐ
|
| 3193 |
+
if 'app_initialized' not in st.session_state:
|
| 3194 |
+
st.session_state.app_initialized = True
|
| 3195 |
+
logger.info("アプリケーション初期化開始")
|
| 3196 |
+
|
| 3197 |
+
# 管理クラスの初期化
|
| 3198 |
+
managers = initialize_all_managers()
|
| 3199 |
+
|
| 3200 |
+
# セッション状態の初期化
|
| 3201 |
+
initialize_session_state(managers)
|
| 3202 |
+
|
| 3203 |
+
# 初期化完了をマーク
|
| 3204 |
+
st.session_state.managers = managers
|
| 3205 |
+
logger.info("初期化完了")
|
| 3206 |
+
else:
|
| 3207 |
+
# 既に初期化済みの場合はmanagersを取得
|
| 3208 |
+
managers = st.session_state.managers
|
| 3209 |
+
|
| 3210 |
+
# CSSの注入
|
| 3211 |
+
inject_custom_css()
|
| 3212 |
+
|
| 3213 |
+
# チャット入力をトップレベルで処理(最優先)
|
| 3214 |
+
handle_chat_input(managers)
|
| 3215 |
+
|
| 3216 |
+
# メインUI表示
|
| 3217 |
+
render_main_ui(managers)
|
| 3218 |
+
|
| 3219 |
+
def render_main_ui(managers):
|
| 3220 |
+
"""メインUIの描画"""
|
| 3221 |
+
# 既存のUI描画コードは後で実装
|
| 3222 |
+
st.title("💬 麻理チャット")
|
| 3223 |
+
st.markdown("*捨てられたアンドロイド「麻理」との対話*")
|
| 3224 |
+
|
| 3225 |
+
# チャット履歴表示
|
| 3226 |
+
if 'chat' in st.session_state and 'messages' in st.session_state.chat:
|
| 3227 |
+
for message in st.session_state.chat['messages']:
|
| 3228 |
+
role = message.get("role", "user")
|
| 3229 |
+
content = message.get("content", "")
|
| 3230 |
+
|
| 3231 |
+
with st.chat_message(role):
|
| 3232 |
+
st.markdown(content)
|
| 3233 |
+
|
| 3234 |
+
def handle_chat_input(managers):
|
| 3235 |
+
"""チャット入力をトップレベルで処理"""
|
| 3236 |
+
# 初期化(セッションステートに必要な変数を登録)
|
| 3237 |
+
if "awaiting_response" not in st.session_state:
|
| 3238 |
+
st.session_state.awaiting_response = False
|
| 3239 |
+
if "latest_prompt" not in st.session_state:
|
| 3240 |
+
st.session_state.latest_prompt = ""
|
| 3241 |
+
|
| 3242 |
+
# チャット入力(トップレベル)
|
| 3243 |
+
user_input = st.chat_input("麻理に話しかける...")
|
| 3244 |
+
|
| 3245 |
+
if user_input and not st.session_state.awaiting_response:
|
| 3246 |
+
st.session_state.latest_prompt = user_input
|
| 3247 |
+
st.session_state.awaiting_response = True
|
| 3248 |
+
|
| 3249 |
+
# rerun回数を制限(無限ループ防止)
|
| 3250 |
+
rerun_count = st.session_state.get('rerun_count', 0)
|
| 3251 |
+
if rerun_count < 5: # 最大5回まで
|
| 3252 |
+
st.session_state.rerun_count = rerun_count + 1
|
| 3253 |
+
logger.info(f"メッセージ送信によるrerun実行 (回数: {st.session_state.rerun_count})")
|
| 3254 |
+
st.rerun()
|
| 3255 |
+
else:
|
| 3256 |
+
logger.warning("rerun回数制限に達しました。処理を継続します。")
|
| 3257 |
+
st.session_state.rerun_count = 0
|
| 3258 |
+
|
| 3259 |
+
# 応答処理(rerun 後にこのブロックが実行される)
|
| 3260 |
+
if st.session_state.awaiting_response:
|
| 3261 |
+
prompt = st.session_state.latest_prompt
|
| 3262 |
+
|
| 3263 |
+
# 1. 入力チェック
|
| 3264 |
+
if len(prompt) > MAX_INPUT_LENGTH:
|
| 3265 |
+
st.error(f"⚠️ メッセージは{MAX_INPUT_LENGTH}文字以内で入力してください。")
|
| 3266 |
+
st.session_state.awaiting_response = False
|
| 3267 |
+
st.session_state.latest_prompt = ""
|
| 3268 |
+
else:
|
| 3269 |
+
# 2. ユーザーのメッセージを履歴に追加
|
| 3270 |
+
if 'chat' not in st.session_state:
|
| 3271 |
+
st.session_state.chat = {'messages': []}
|
| 3272 |
+
if 'messages' not in st.session_state.chat:
|
| 3273 |
+
st.session_state.chat['messages'] = []
|
| 3274 |
+
|
| 3275 |
+
st.session_state.chat['messages'].append({
|
| 3276 |
+
"role": "user",
|
| 3277 |
+
"content": prompt
|
| 3278 |
+
})
|
| 3279 |
+
|
| 3280 |
+
# 3. AI応答生成
|
| 3281 |
+
with st.spinner("麻理が考え中..."):
|
| 3282 |
+
response = process_chat_message(prompt, managers)
|
| 3283 |
+
|
| 3284 |
+
# 4. AI応答を履歴に追加
|
| 3285 |
+
st.session_state.chat['messages'].append({
|
| 3286 |
+
"role": "assistant",
|
| 3287 |
+
"content": response
|
| 3288 |
+
})
|
| 3289 |
+
|
| 3290 |
+
# 5. 応答完了状態に戻す
|
| 3291 |
+
st.session_state.awaiting_response = False
|
| 3292 |
+
st.session_state.latest_prompt = ""
|
| 3293 |
+
st.session_state.rerun_count = 0
|
| 3294 |
+
|
| 3295 |
+
# 再描画
|
| 3296 |
+
st.rerun()
|
| 3297 |
+
|
| 3298 |
+
def process_chat_message(message: str, managers):
|
| 3299 |
+
"""チャットメッセージ処理"""
|
| 3300 |
+
try:
|
| 3301 |
+
logger.info(f"🚀 process_chat_message開始 - メッセージ: '{message}'")
|
| 3302 |
+
|
| 3303 |
+
# 簡単な応答生成(テスト用)
|
| 3304 |
+
return f"[HIDDEN:({message}について考えている)]そうね、{message}について話すのね。"
|
| 3305 |
+
|
| 3306 |
+
except Exception as e:
|
| 3307 |
+
logger.error(f"チャットメッセージ処理エラー: {e}")
|
| 3308 |
+
return "[HIDDEN:(システムエラーが発生した)]ごめん、何か問題が起きたみたい。"
|
| 3309 |
+
|
| 3310 |
if __name__ == "__main__":
|
| 3311 |
if not Config.validate_config():
|
| 3312 |
logger.critical("手紙機能の設定にエラーがあります。アプリケーションを起動できません。")
|
persistent_user_manager.py
CHANGED
|
@@ -80,32 +80,43 @@ class PersistentUserManager:
|
|
| 80 |
|
| 81 |
def get_or_create_user_id(self) -> str:
|
| 82 |
"""
|
| 83 |
-
ユーザーIDを取得または新規作成(Cookie
|
| 84 |
|
| 85 |
Returns:
|
| 86 |
ユーザーID
|
| 87 |
"""
|
| 88 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
# 1. CookieからユーザーIDを取得
|
| 90 |
user_id = self._get_user_id_from_cookie()
|
| 91 |
|
| 92 |
if user_id and self._is_valid_user_id(user_id):
|
| 93 |
# 有効なユーザーIDが存在
|
| 94 |
self._update_user_access_time(user_id)
|
| 95 |
-
|
|
|
|
|
|
|
| 96 |
return user_id
|
| 97 |
|
| 98 |
-
# 2.
|
| 99 |
user_id = st.session_state.get('persistent_user_id')
|
| 100 |
if user_id and self._is_valid_user_id(user_id):
|
| 101 |
# セッション状態にあるIDを使用してCookieを更新
|
| 102 |
self._set_user_id_cookie(user_id)
|
| 103 |
self._update_user_access_time(user_id)
|
|
|
|
| 104 |
logger.info(f"セッション状態からユーザーID復元: {user_id[:8]}...")
|
| 105 |
return user_id
|
| 106 |
|
| 107 |
# 3. 新規ユーザーIDを作成
|
| 108 |
user_id = self._create_new_user()
|
|
|
|
| 109 |
logger.info(f"新規ユーザーID作成: {user_id[:8]}...")
|
| 110 |
return user_id
|
| 111 |
|
|
@@ -114,6 +125,7 @@ class PersistentUserManager:
|
|
| 114 |
# フォールバック: 一時的なIDを生成
|
| 115 |
fallback_id = str(uuid.uuid4())
|
| 116 |
st.session_state.persistent_user_id = fallback_id
|
|
|
|
| 117 |
return fallback_id
|
| 118 |
|
| 119 |
def _get_user_id_from_cookie(self) -> Optional[str]:
|
|
|
|
| 80 |
|
| 81 |
def get_or_create_user_id(self) -> str:
|
| 82 |
"""
|
| 83 |
+
ユーザーIDを取得または新規作成(Cookie連携・重複防止)
|
| 84 |
|
| 85 |
Returns:
|
| 86 |
ユーザーID
|
| 87 |
"""
|
| 88 |
try:
|
| 89 |
+
# 重複チェック防止: 既にセッション状態にある場合はそれを使用
|
| 90 |
+
if hasattr(st.session_state, 'persistent_user_id_checked'):
|
| 91 |
+
existing_id = st.session_state.get('persistent_user_id')
|
| 92 |
+
if existing_id and self._is_valid_uuid(existing_id):
|
| 93 |
+
logger.debug(f"既にチェック済みのユーザーID使用: {existing_id[:8]}...")
|
| 94 |
+
return existing_id
|
| 95 |
+
|
| 96 |
# 1. CookieからユーザーIDを取得
|
| 97 |
user_id = self._get_user_id_from_cookie()
|
| 98 |
|
| 99 |
if user_id and self._is_valid_user_id(user_id):
|
| 100 |
# 有効なユーザーIDが存在
|
| 101 |
self._update_user_access_time(user_id)
|
| 102 |
+
st.session_state.persistent_user_id = user_id
|
| 103 |
+
st.session_state.persistent_user_id_checked = True
|
| 104 |
+
logger.info(f"CookieからユーザーID取得: {user_id[:8]}...")
|
| 105 |
return user_id
|
| 106 |
|
| 107 |
+
# 2. セッション状態から取得を試行(Cookieが無効な場合のみ)
|
| 108 |
user_id = st.session_state.get('persistent_user_id')
|
| 109 |
if user_id and self._is_valid_user_id(user_id):
|
| 110 |
# セッション状態にあるIDを使用してCookieを更新
|
| 111 |
self._set_user_id_cookie(user_id)
|
| 112 |
self._update_user_access_time(user_id)
|
| 113 |
+
st.session_state.persistent_user_id_checked = True
|
| 114 |
logger.info(f"セッション状態からユーザーID復元: {user_id[:8]}...")
|
| 115 |
return user_id
|
| 116 |
|
| 117 |
# 3. 新規ユーザーIDを作成
|
| 118 |
user_id = self._create_new_user()
|
| 119 |
+
st.session_state.persistent_user_id_checked = True
|
| 120 |
logger.info(f"新規ユーザーID作成: {user_id[:8]}...")
|
| 121 |
return user_id
|
| 122 |
|
|
|
|
| 125 |
# フォールバック: 一時的なIDを生成
|
| 126 |
fallback_id = str(uuid.uuid4())
|
| 127 |
st.session_state.persistent_user_id = fallback_id
|
| 128 |
+
st.session_state.persistent_user_id_checked = True
|
| 129 |
return fallback_id
|
| 130 |
|
| 131 |
def _get_user_id_from_cookie(self) -> Optional[str]:
|