Spaces:
Runtime error
Runtime error
Upload 3 files
Browse files- main_app.py +26 -15
- persistent_user_manager.py +84 -73
- requirements.txt +3 -2
main_app.py
CHANGED
|
@@ -19,6 +19,20 @@ if sys.platform.startswith('win'):
|
|
| 19 |
# .envファイルから環境変数を読み込み
|
| 20 |
load_dotenv()
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
# ロガー設定
|
| 23 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 24 |
logger = logging.getLogger(__name__)
|
|
@@ -504,32 +518,29 @@ def initialize_session_state(managers, force_reset_override=False):
|
|
| 504 |
user_id_manager = managers["user_id_manager"] # フォールバック用
|
| 505 |
session_api_client = managers["session_api_client"]
|
| 506 |
|
| 507 |
-
#
|
| 508 |
# 重複ID生成防止: 既にuser_idがあり、force_resetでない場合は既存IDを使用
|
| 509 |
if 'user_id' in st.session_state and not force_reset:
|
| 510 |
session_id = st.session_state.user_id
|
| 511 |
logger.debug(f"既存ユーザーID使用(rerun対応): {session_id[:8]}...")
|
| 512 |
else:
|
| 513 |
-
#
|
| 514 |
-
session_id = None
|
| 515 |
-
|
| 516 |
try:
|
| 517 |
-
#
|
| 518 |
-
session_id = persistent_user_manager.get_or_create_user_id()
|
| 519 |
-
logger.info(f"
|
| 520 |
except Exception as e:
|
| 521 |
-
logger.
|
| 522 |
|
|
|
|
| 523 |
try:
|
| 524 |
-
# フォールバック: 従来のローカルファイル方式
|
| 525 |
session_id = user_id_manager.get_or_create_user_id()
|
| 526 |
-
logger.
|
| 527 |
except Exception as e2:
|
| 528 |
-
logger.error(f"
|
| 529 |
-
# 最終フォールバック:
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
logger.warning(f"最終フォールバック: 一時的なIDを生成: {session_id[:8]}...")
|
| 533 |
|
| 534 |
# ユーザーIDとしてセッションIDを使用
|
| 535 |
session_changed = ('user_id' not in st.session_state or
|
|
|
|
| 19 |
# .envファイルから環境変数を読み込み
|
| 20 |
load_dotenv()
|
| 21 |
|
| 22 |
+
# PyTorchのクラス登録問題を回避するための設定
|
| 23 |
+
try:
|
| 24 |
+
import torch
|
| 25 |
+
# スレッド数を制限してクラス登録問題を回避
|
| 26 |
+
torch.set_num_threads(int(os.getenv("TORCH_NUM_THREADS", "1")))
|
| 27 |
+
# 警告を抑制
|
| 28 |
+
import warnings
|
| 29 |
+
warnings.filterwarnings("ignore", category=UserWarning, module="torch")
|
| 30 |
+
logger.info("PyTorch初期化設定完了")
|
| 31 |
+
except ImportError:
|
| 32 |
+
logger.info("PyTorchが利用できません - ルールベース処理を使用")
|
| 33 |
+
except Exception as e:
|
| 34 |
+
logger.warning(f"PyTorch初期化設定エラー: {e}")
|
| 35 |
+
|
| 36 |
# ロガー設定
|
| 37 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 38 |
logger = logging.getLogger(__name__)
|
|
|
|
| 518 |
user_id_manager = managers["user_id_manager"] # フォールバック用
|
| 519 |
session_api_client = managers["session_api_client"]
|
| 520 |
|
| 521 |
+
# Cookie認証ベースのユーザーID取得(起動時uuid4()生成を排除)
|
| 522 |
# 重複ID生成防止: 既にuser_idがあり、force_resetでない場合は既存IDを使用
|
| 523 |
if 'user_id' in st.session_state and not force_reset:
|
| 524 |
session_id = st.session_state.user_id
|
| 525 |
logger.debug(f"既存ユーザーID使用(rerun対応): {session_id[:8]}...")
|
| 526 |
else:
|
| 527 |
+
# Cookie認証ベースでユーザーIDを取得(新規作成またはリセット時のみ)
|
|
|
|
|
|
|
| 528 |
try:
|
| 529 |
+
# Cookie認証を最優先で使用
|
| 530 |
+
session_id = persistent_user_manager.get_or_create_user_id(force_reset=force_reset)
|
| 531 |
+
logger.info(f"Cookie認証ベースユーザーID取得: {session_id[:8]}...")
|
| 532 |
except Exception as e:
|
| 533 |
+
logger.error(f"Cookie認証失敗: {e}")
|
| 534 |
|
| 535 |
+
# フォールバック: 従来のローカルファイル方式(uuid4()生成を避ける)
|
| 536 |
try:
|
|
|
|
| 537 |
session_id = user_id_manager.get_or_create_user_id()
|
| 538 |
+
logger.warning(f"フォールバック: ローカルファイル方式使用: {session_id[:8]}...")
|
| 539 |
except Exception as e2:
|
| 540 |
+
logger.error(f"全ての認証方式が失敗: {e2}")
|
| 541 |
+
# 最終フォールバック: セッション固有の一時的なIDを生成(起動時uuid4()を避ける)
|
| 542 |
+
session_id = f"temp_{id(st.session_state)}"
|
| 543 |
+
logger.warning(f"最終フォールバック: セッション固有一時ID: {session_id}")
|
|
|
|
| 544 |
|
| 545 |
# ユーザーIDとしてセッションIDを使用
|
| 546 |
session_changed = ('user_id' not in st.session_state or
|
persistent_user_manager.py
CHANGED
|
@@ -57,19 +57,21 @@ class PersistentUserManager:
|
|
| 57 |
logger.warning(f"フォールバック: ローカルディレクトリを使用 {self.user_data_dir}")
|
| 58 |
|
| 59 |
def _ensure_cookie_manager(self, force_init: bool = False):
|
| 60 |
-
"""Cookie
|
| 61 |
# 既に初期化済みで、強制初期化でない場合はスキップ
|
| 62 |
if self._cookie_initialized and not force_init:
|
|
|
|
| 63 |
return
|
| 64 |
|
| 65 |
-
#
|
| 66 |
-
|
| 67 |
-
|
|
|
|
| 68 |
self._cookie_initialized = True
|
| 69 |
return
|
| 70 |
|
| 71 |
try:
|
| 72 |
-
logger.info("Cookie
|
| 73 |
|
| 74 |
# セキュアなパスワードを生成(環境変数から取得、なければ生成)
|
| 75 |
cookie_password = os.getenv("MARI_COOKIE_PASSWORD", "mari_chat_secure_key_2024")
|
|
@@ -81,26 +83,27 @@ class PersistentUserManager:
|
|
| 81 |
|
| 82 |
# Cookieが準備できるまで待機
|
| 83 |
if not cookies.ready():
|
|
|
|
| 84 |
st.stop()
|
| 85 |
|
| 86 |
self.cookies = cookies
|
| 87 |
self._cookie_initialized = True
|
| 88 |
|
| 89 |
-
#
|
| 90 |
-
st.session_state
|
| 91 |
|
| 92 |
-
logger.info("Cookie
|
| 93 |
|
| 94 |
except Exception as e:
|
| 95 |
logger.error(f"Cookie管理初期化エラー: {e}")
|
| 96 |
# フォールバック: セッション状態のみ使用
|
| 97 |
self.cookies = None
|
| 98 |
self._cookie_initialized = True
|
| 99 |
-
st.session_state
|
| 100 |
|
| 101 |
def get_or_create_user_id(self, force_reset: bool = False) -> str:
|
| 102 |
"""
|
| 103 |
-
|
| 104 |
|
| 105 |
Args:
|
| 106 |
force_reset: フルリセット時のみTrue
|
|
@@ -109,69 +112,52 @@ class PersistentUserManager:
|
|
| 109 |
ユーザーID
|
| 110 |
"""
|
| 111 |
try:
|
| 112 |
-
#
|
| 113 |
if not force_reset and 'user_id' in st.session_state:
|
| 114 |
existing_id = st.session_state.user_id
|
| 115 |
if existing_id and self._is_valid_uuid(existing_id):
|
| 116 |
-
logger.debug(f"
|
| 117 |
return existing_id
|
| 118 |
|
| 119 |
-
#
|
| 120 |
-
|
| 121 |
-
existing_id = st.session_state.get('persistent_user_id')
|
| 122 |
-
if existing_id and self._is_valid_uuid(existing_id):
|
| 123 |
-
logger.debug(f"既にチェック済みのユーザーID使用: {existing_id[:8]}...")
|
| 124 |
-
return existing_id
|
| 125 |
|
| 126 |
-
# Cookie
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
st.session_state.persistent_user_id = user_id
|
| 135 |
-
st.session_state.persistent_user_id_checked = True
|
| 136 |
-
logger.info(f"CookieからユーザーID取得: {user_id[:8]}...")
|
| 137 |
-
return user_id
|
| 138 |
-
|
| 139 |
-
# 2. セッション状態から取得を試行(Cookieが無効な場合のみ)
|
| 140 |
-
if not force_reset:
|
| 141 |
-
user_id = st.session_state.get('persistent_user_id')
|
| 142 |
-
if user_id and self._is_valid_user_id(user_id):
|
| 143 |
-
# セッション状態にあるIDを使用してCookieを更新
|
| 144 |
-
self._set_user_id_cookie(user_id)
|
| 145 |
-
self._update_user_access_time(user_id)
|
| 146 |
-
st.session_state.persistent_user_id_checked = True
|
| 147 |
-
logger.info(f"セッション状態からユーザーID復元: {user_id[:8]}...")
|
| 148 |
-
return user_id
|
| 149 |
-
|
| 150 |
-
# 3. 新規ユーザーIDを作成
|
| 151 |
-
user_id = self._create_new_user()
|
| 152 |
-
st.session_state.persistent_user_id_checked = True
|
| 153 |
-
logger.info(f"新規ユーザーID作成: {user_id[:8]}...")
|
| 154 |
return user_id
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
|
|
|
| 166 |
return user_id
|
| 167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
except Exception as e:
|
| 169 |
-
logger.error(f"
|
| 170 |
-
#
|
| 171 |
-
|
| 172 |
-
st.session_state.
|
| 173 |
-
|
| 174 |
-
return
|
| 175 |
|
| 176 |
def _get_user_id_from_cookie(self) -> Optional[str]:
|
| 177 |
"""CookieからユーザーIDを取得"""
|
|
@@ -239,8 +225,8 @@ class PersistentUserManager:
|
|
| 239 |
logger.warning(f"ユーザーID検証エラー: {e}")
|
| 240 |
return False
|
| 241 |
|
| 242 |
-
def
|
| 243 |
-
"""
|
| 244 |
try:
|
| 245 |
user_id = str(uuid.uuid4())
|
| 246 |
|
|
@@ -250,6 +236,7 @@ class PersistentUserManager:
|
|
| 250 |
"created_at": datetime.now().isoformat(),
|
| 251 |
"last_access": datetime.now().isoformat(),
|
| 252 |
"version": "1.0",
|
|
|
|
| 253 |
"game_data": {
|
| 254 |
"affection": 30,
|
| 255 |
"messages": [{"role": "assistant", "content": "何の用?遊びに来たの?", "is_initial": True}],
|
|
@@ -267,19 +254,43 @@ class PersistentUserManager:
|
|
| 267 |
with open(user_file, 'w', encoding='utf-8') as f:
|
| 268 |
json.dump(user_data, f, ensure_ascii=False, indent=2)
|
| 269 |
|
| 270 |
-
# Cookie
|
| 271 |
self._set_user_id_cookie(user_id)
|
| 272 |
-
st.session_state.persistent_user_id = user_id
|
| 273 |
|
| 274 |
-
|
|
|
|
|
|
|
|
|
|
| 275 |
return user_id
|
| 276 |
|
| 277 |
except Exception as e:
|
| 278 |
-
logger.error(f"
|
| 279 |
-
#
|
| 280 |
-
|
| 281 |
-
st.session_state.
|
| 282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
|
| 284 |
def _update_user_access_time(self, user_id: str):
|
| 285 |
"""ユーザーの最終アクセス時刻を更新"""
|
|
|
|
| 57 |
logger.warning(f"フォールバック: ローカルディレクトリを使用 {self.user_data_dir}")
|
| 58 |
|
| 59 |
def _ensure_cookie_manager(self, force_init: bool = False):
|
| 60 |
+
"""Cookie管理システムの初期化(ブラウザごとに一度だけ)"""
|
| 61 |
# 既に初期化済みで、強制初期化でない場合はスキップ
|
| 62 |
if self._cookie_initialized and not force_init:
|
| 63 |
+
logger.debug("Cookie管理システム既に初期化済み")
|
| 64 |
return
|
| 65 |
|
| 66 |
+
# ブラウザセッション単位でのCookie初期化チェック
|
| 67 |
+
browser_session_key = f"cookie_initialized_{id(st.session_state)}"
|
| 68 |
+
if not force_init and st.session_state.get(browser_session_key, False):
|
| 69 |
+
logger.debug("このブラウザセッションでCookie管理システム既に初期化済み")
|
| 70 |
self._cookie_initialized = True
|
| 71 |
return
|
| 72 |
|
| 73 |
try:
|
| 74 |
+
logger.info("Cookie管理システム初期化開始(ブラウザセッション単位)...")
|
| 75 |
|
| 76 |
# セキュアなパスワードを生成(環境変数から取得、なければ生成)
|
| 77 |
cookie_password = os.getenv("MARI_COOKIE_PASSWORD", "mari_chat_secure_key_2024")
|
|
|
|
| 83 |
|
| 84 |
# Cookieが準備できるまで待機
|
| 85 |
if not cookies.ready():
|
| 86 |
+
logger.warning("Cookie準備中 - 待機")
|
| 87 |
st.stop()
|
| 88 |
|
| 89 |
self.cookies = cookies
|
| 90 |
self._cookie_initialized = True
|
| 91 |
|
| 92 |
+
# ブラウザセッション単位でフラグを設定
|
| 93 |
+
st.session_state[browser_session_key] = True
|
| 94 |
|
| 95 |
+
logger.info("Cookie管理システム初期化完了(ブラウザセッション単位)")
|
| 96 |
|
| 97 |
except Exception as e:
|
| 98 |
logger.error(f"Cookie管理初期化エラー: {e}")
|
| 99 |
# フォールバック: セッション状態のみ使用
|
| 100 |
self.cookies = None
|
| 101 |
self._cookie_initialized = True
|
| 102 |
+
st.session_state[browser_session_key] = True
|
| 103 |
|
| 104 |
def get_or_create_user_id(self, force_reset: bool = False) -> str:
|
| 105 |
"""
|
| 106 |
+
Cookie認証ベースのユーザーID取得(ブラウザごとに一つのCookie)
|
| 107 |
|
| 108 |
Args:
|
| 109 |
force_reset: フルリセット時のみTrue
|
|
|
|
| 112 |
ユーザーID
|
| 113 |
"""
|
| 114 |
try:
|
| 115 |
+
# フルリセット時以外で、既にセッション状態にuser_idがある場合はそれを使用
|
| 116 |
if not force_reset and 'user_id' in st.session_state:
|
| 117 |
existing_id = st.session_state.user_id
|
| 118 |
if existing_id and self._is_valid_uuid(existing_id):
|
| 119 |
+
logger.debug(f"セッション状態からユーザーID使用: {existing_id[:8]}...")
|
| 120 |
return existing_id
|
| 121 |
|
| 122 |
+
# Cookie管理システムを初期化(ブラウザごとに一度だけ)
|
| 123 |
+
self._ensure_cookie_manager(force_init=force_reset)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
# 1. CookieからユーザーIDを取得(最優先)
|
| 126 |
+
user_id = self._get_user_id_from_cookie()
|
| 127 |
+
|
| 128 |
+
if user_id and self._is_valid_user_id(user_id):
|
| 129 |
+
# 有効なCookieベースのユーザーIDが存在
|
| 130 |
+
self._update_user_access_time(user_id)
|
| 131 |
+
st.session_state.user_id = user_id # セッション状態に保存
|
| 132 |
+
logger.info(f"Cookie認証成功: {user_id[:8]}...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
return user_id
|
| 134 |
+
|
| 135 |
+
# 2. Cookieが無効または存在しない場合は新規作成
|
| 136 |
+
if force_reset or not user_id:
|
| 137 |
+
user_id = self._create_new_user_with_cookie()
|
| 138 |
+
logger.info(f"新規Cookie認証ユーザー作成: {user_id[:8]}...")
|
| 139 |
+
return user_id
|
| 140 |
+
|
| 141 |
+
# 3. 最終フォールバック(Cookieが無効だが何らかのIDがある場合)
|
| 142 |
+
if user_id and self._is_valid_uuid(user_id):
|
| 143 |
+
# UUIDとして有効だが、ユーザーファイルが存在しない場合は新規作成
|
| 144 |
+
user_id = self._create_new_user_with_cookie()
|
| 145 |
+
logger.info(f"フォールバック新規ユーザー作成: {user_id[:8]}...")
|
| 146 |
return user_id
|
| 147 |
|
| 148 |
+
# 4. 完全フォールバック(Cookie無効時)
|
| 149 |
+
logger.warning("Cookie認証失敗 - 一時的なセッションIDを使用")
|
| 150 |
+
temp_id = str(uuid.uuid4())
|
| 151 |
+
st.session_state.user_id = temp_id
|
| 152 |
+
return temp_id
|
| 153 |
+
|
| 154 |
except Exception as e:
|
| 155 |
+
logger.error(f"Cookie認証エラー: {e}")
|
| 156 |
+
# 完全フォールバック: 一時的なIDを生成(Cookieに保存しない)
|
| 157 |
+
temp_id = str(uuid.uuid4())
|
| 158 |
+
st.session_state.user_id = temp_id
|
| 159 |
+
logger.warning(f"一時的なセッションID使用: {temp_id[:8]}...")
|
| 160 |
+
return temp_id
|
| 161 |
|
| 162 |
def _get_user_id_from_cookie(self) -> Optional[str]:
|
| 163 |
"""CookieからユーザーIDを取得"""
|
|
|
|
| 225 |
logger.warning(f"ユーザーID検証エラー: {e}")
|
| 226 |
return False
|
| 227 |
|
| 228 |
+
def _create_new_user_with_cookie(self) -> str:
|
| 229 |
+
"""Cookie認証ベースの新���ユーザーを作成"""
|
| 230 |
try:
|
| 231 |
user_id = str(uuid.uuid4())
|
| 232 |
|
|
|
|
| 236 |
"created_at": datetime.now().isoformat(),
|
| 237 |
"last_access": datetime.now().isoformat(),
|
| 238 |
"version": "1.0",
|
| 239 |
+
"browser_fingerprint": self._generate_browser_fingerprint(),
|
| 240 |
"game_data": {
|
| 241 |
"affection": 30,
|
| 242 |
"messages": [{"role": "assistant", "content": "何の用?遊びに来たの?", "is_initial": True}],
|
|
|
|
| 254 |
with open(user_file, 'w', encoding='utf-8') as f:
|
| 255 |
json.dump(user_data, f, ensure_ascii=False, indent=2)
|
| 256 |
|
| 257 |
+
# Cookieに保存(ブラウザごとに一つ)
|
| 258 |
self._set_user_id_cookie(user_id)
|
|
|
|
| 259 |
|
| 260 |
+
# セッション状態に保存
|
| 261 |
+
st.session_state.user_id = user_id
|
| 262 |
+
|
| 263 |
+
logger.info(f"Cookie認証ベース新規ユーザー作成完了: {user_id[:8]}...")
|
| 264 |
return user_id
|
| 265 |
|
| 266 |
except Exception as e:
|
| 267 |
+
logger.error(f"Cookie認証ベース新規ユーザー作成エラー: {e}")
|
| 268 |
+
# フォールバック: 一時的なIDを生成(Cookieに保存しない)
|
| 269 |
+
temp_id = str(uuid.uuid4())
|
| 270 |
+
st.session_state.user_id = temp_id
|
| 271 |
+
logger.warning(f"フォールバック一時ID: {temp_id[:8]}...")
|
| 272 |
+
return temp_id
|
| 273 |
+
|
| 274 |
+
def _create_new_user(self) -> str:
|
| 275 |
+
"""従来の新規ユーザー作成(後方互換性のため残す)"""
|
| 276 |
+
return self._create_new_user_with_cookie()
|
| 277 |
+
|
| 278 |
+
def _generate_browser_fingerprint(self) -> str:
|
| 279 |
+
"""ブラウザフィンガープリントを生成(簡易版)"""
|
| 280 |
+
try:
|
| 281 |
+
# Streamlitのセッション情報を使用してフィンガープリントを生成
|
| 282 |
+
import hashlib
|
| 283 |
+
|
| 284 |
+
# セッション固有の情報を組み合わせ
|
| 285 |
+
session_info = f"{id(st.session_state)}_{datetime.now().strftime('%Y%m%d')}"
|
| 286 |
+
fingerprint = hashlib.md5(session_info.encode()).hexdigest()[:16]
|
| 287 |
+
|
| 288 |
+
logger.debug(f"ブラウザフィンガープリント生成: {fingerprint}")
|
| 289 |
+
return fingerprint
|
| 290 |
+
|
| 291 |
+
except Exception as e:
|
| 292 |
+
logger.warning(f"ブラウザフィンガープリント生成エラー: {e}")
|
| 293 |
+
return "unknown_browser"
|
| 294 |
|
| 295 |
def _update_user_access_time(self, user_id: str):
|
| 296 |
"""ユーザーの最終アクセス時刻を更新"""
|
requirements.txt
CHANGED
|
@@ -6,8 +6,9 @@ groq>=0.4.0
|
|
| 6 |
requests>=2.31.0
|
| 7 |
unidic-lite
|
| 8 |
typing-extensions>=4.0.0
|
| 9 |
-
|
| 10 |
-
torch>=1.12.0
|
|
|
|
| 11 |
sentencepiece>=0.1.95
|
| 12 |
fugashi>=1.1.0
|
| 13 |
ipadic>=1.0.0
|
|
|
|
| 6 |
requests>=2.31.0
|
| 7 |
unidic-lite
|
| 8 |
typing-extensions>=4.0.0
|
| 9 |
+
# PyTorchとTransformers(クラス登録問題対応版)
|
| 10 |
+
torch>=1.12.0,<2.0.0
|
| 11 |
+
transformers>=4.20.0,<5.0.0
|
| 12 |
sentencepiece>=0.1.95
|
| 13 |
fugashi>=1.1.0
|
| 14 |
ipadic>=1.0.0
|