#!/usr/bin/env python3 """ パス管理モジュール PyInstaller対応とサーバー起動の両方に対応 """ import os import sys from pathlib import Path from typing import Optional class PathManager: """パス管理クラス(シングルトン)""" _instance: Optional["PathManager"] = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): if self._initialized: return self._initialized = True self._setup_paths() def _setup_paths(self): """パス設定を初期化""" # 実行環境の判定 if getattr(sys, 'frozen', False): # PyInstallerでビルドされた場合 self.is_pyinstaller = True self.base_path = Path(sys._MEIPASS) self.package_path = self.base_path / 'package' self.python_executable = sys.executable else: # 開発環境の場合 self.is_pyinstaller = False self.base_path = Path(__file__).parent.parent self.package_path = self.base_path / 'package' self.python_executable = sys.executable # モデルパス設定 self.model_path = self._get_model_path() # ログ出力 print(f"[PATH] PyInstaller: {self.is_pyinstaller}") print(f"[PATH] Base path: {self.base_path}") print(f"[PATH] Package path: {self.package_path}") print(f"[PATH] Model path: {self.model_path}") def _get_model_path(self) -> str: """モデルファイルのパスを取得""" # 環境変数から取得 env_model_path = os.getenv('LLM_MODEL_PATH') if env_model_path and os.path.exists(env_model_path): return env_model_path # 配布物からの相対位置(優先) try: # 実行ファイルのあるディレクトリ(PyInstaller実行時は dist/.../tauri_python_server の場所) executable_dir = Path(os.path.dirname(sys.executable)) if getattr(sys, 'frozen', False) else Path(__file__).parent.parent candidate_relative_paths = [ # 配布レイアウト: /models/ (exe が /python/ にある想定) (executable_dir.parent / 'models' / 'llama-3.2-3b-instruct-q4_k_m.gguf'), # exe と同階層に models/ (executable_dir / 'models' / 'llama-3.2-3b-instruct-q4_k_m.gguf'), # MEIPASS(展開一時ディレクトリ)配下 (Path(getattr(sys, '_MEIPASS', str(self.base_path))) / 'models' / 'llama-3.2-3b-instruct-q4_k_m.gguf'), ] for rel in candidate_relative_paths: rel_str = str(rel) if os.path.exists(rel_str): return rel_str except Exception: pass # デフォルトパス default_paths = [ os.path.expanduser("~/Documents/GitHub/LLMV_app_frontend/src-tauri/python/models/llama-3.2-3b-instruct-q4_k_m.gguf"), os.path.expanduser("~/Documents/models/llama-3.2-3b-instruct-q4_k_m.gguf"), os.path.expanduser("~/models/llama-3.2-3b-instruct-q4_k_m.gguf"), "/opt/models/llama-3.2-3b-instruct-q4_k_m.gguf" ] for path in default_paths: if os.path.exists(path): return path # 見つからない場合は最初のデフォルトパスを返す return default_paths[0] def get_package_path(self) -> str: """パッケージパスを取得""" return str(self.package_path) def get_model_path(self) -> str: """モデルパスを取得""" return self.model_path def get_python_executable(self) -> str: """Python実行ファイルのパスを取得""" return self.python_executable def setup_sys_path(self): """sys.pathを設定""" package_path_str = self.get_package_path() # パッケージパスを追加 if package_path_str not in sys.path: sys.path.insert(0, package_path_str) # conda環境のパッケージパスも追加(開発環境のみ) if not self.is_pyinstaller: conda_package_path = '/Users/wataru/Documents/AppProject/LLMView_Server/package' if os.path.exists(conda_package_path) and conda_package_path not in sys.path: sys.path.insert(0, conda_package_path) print(f"[PATH] sys.path configured: {len(sys.path)} paths") def get_server_config(self) -> dict: """サーバー設定を取得""" return { 'host': '127.0.0.1', 'port': 5000, 'debug': False, 'use_reloader': False, 'threaded': True } def get_pyinstaller_spec_path(self) -> str: """PyInstaller specファイルのパスを取得""" return str(self.base_path / 'tauri_python_server.spec') def get_dist_path(self) -> str: """ビルド出力ディレクトリのパスを取得""" return str(self.base_path / 'dist') def get_executable_name(self) -> str: """実行ファイル名を取得""" return 'tauri_python_server' # グローバルインスタンス path_manager = PathManager() def get_path_manager() -> PathManager: """PathManagerインスタンスを取得""" return path_manager