256 lines
9.4 KiB
Python
256 lines
9.4 KiB
Python
"""
|
|
Settings management
|
|
애플리케이션 설정 관리 모듈
|
|
|
|
- 일반 앱 설정: ~/.mmi_analyzer/settings.json
|
|
- AI 설정(키 포함): ~/.mmi_analyzer/ai_settings.json (별도 파일)
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, Any
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass
|
|
class AIProviderConfig:
|
|
"""개별 AI 프로바이더 설정"""
|
|
api_key: str = ""
|
|
model: str = ""
|
|
enabled: bool = False
|
|
|
|
|
|
@dataclass
|
|
class AISettings:
|
|
"""AI 관련 설정"""
|
|
current_provider: str = "" # 현재 선택된 프로바이더
|
|
providers: Dict[str, Dict[str, Any]] = field(default_factory=dict)
|
|
|
|
def __post_init__(self):
|
|
# 기본 프로바이더 설정 초기화
|
|
default_providers = {
|
|
"openai": {"api_key": "", "model": "gpt-4o-mini", "enabled": False},
|
|
"openrouter": {"api_key": "", "model": "openai/gpt-4o-mini", "enabled": False},
|
|
"gemini": {"api_key": "", "model": "gemini-1.5-flash", "enabled": False},
|
|
"xai": {"api_key": "", "model": "grok-beta", "enabled": False},
|
|
}
|
|
for key, value in default_providers.items():
|
|
if key not in self.providers:
|
|
self.providers[key] = value
|
|
|
|
def get_api_key(self, provider_type: str) -> str:
|
|
"""프로바이더의 API 키 반환"""
|
|
provider_key = provider_type.value if hasattr(provider_type, 'value') else provider_type
|
|
if provider_key in self.providers:
|
|
return self.providers[provider_key].get("api_key", "")
|
|
return ""
|
|
|
|
def set_api_key(self, provider_type: str, api_key: str):
|
|
"""프로바이더의 API 키 설정"""
|
|
provider_key = provider_type.value if hasattr(provider_type, 'value') else provider_type
|
|
if provider_key not in self.providers:
|
|
self.providers[provider_key] = {}
|
|
self.providers[provider_key]["api_key"] = api_key
|
|
if api_key:
|
|
self.providers[provider_key]["enabled"] = True
|
|
|
|
def get_model(self, provider_type: str) -> str:
|
|
"""프로바이더의 모델 반환"""
|
|
provider_key = provider_type.value if hasattr(provider_type, 'value') else provider_type
|
|
if provider_key in self.providers:
|
|
return self.providers[provider_key].get("model", "")
|
|
return ""
|
|
|
|
def set_model(self, provider_type: str, model: str):
|
|
"""프로바이더의 모델 설정"""
|
|
provider_key = provider_type.value if hasattr(provider_type, 'value') else provider_type
|
|
if provider_key not in self.providers:
|
|
self.providers[provider_key] = {}
|
|
self.providers[provider_key]["model"] = model
|
|
|
|
def get_current_provider(self) -> Optional[str]:
|
|
"""현재 선택된 프로바이더 타입 반환"""
|
|
return self.current_provider if self.current_provider else None
|
|
|
|
def set_current_provider(self, provider_type: str):
|
|
"""현재 프로바이더 설정"""
|
|
provider_key = provider_type.value if hasattr(provider_type, 'value') else provider_type
|
|
self.current_provider = provider_key
|
|
|
|
def is_provider_configured(self, provider_type: str) -> bool:
|
|
"""프로바이더가 설정되었는지 확인"""
|
|
provider_key = provider_type.value if hasattr(provider_type, 'value') else provider_type
|
|
if provider_key in self.providers:
|
|
api_key = self.providers[provider_key].get("api_key", "")
|
|
return bool(api_key)
|
|
return False
|
|
|
|
|
|
@dataclass
|
|
class AppSettings:
|
|
"""전체 애플리케이션 설정"""
|
|
ai: AISettings = field(default_factory=AISettings)
|
|
theme: str = "dark"
|
|
language: str = "ko"
|
|
window_geometry: Dict[str, int] = field(default_factory=lambda: {
|
|
"x": 100, "y": 100, "width": 1800, "height": 1000
|
|
})
|
|
recent_files: list = field(default_factory=list)
|
|
max_recent_files: int = 10
|
|
|
|
|
|
class SettingsManager:
|
|
"""
|
|
설정 관리자
|
|
설정 파일 저장/로드 및 관리
|
|
"""
|
|
|
|
DEFAULT_SETTINGS_DIR = Path.home() / ".mmi_analyzer"
|
|
APP_SETTINGS_FILE = "settings.json"
|
|
AI_SETTINGS_FILE = "ai_settings.json"
|
|
|
|
def __init__(self, settings_dir: Optional[Path] = None):
|
|
self._settings_dir = settings_dir or self.DEFAULT_SETTINGS_DIR
|
|
self._app_settings_file = self._settings_dir / self.APP_SETTINGS_FILE
|
|
self._ai_settings_file = self._settings_dir / self.AI_SETTINGS_FILE
|
|
self._settings: Optional[AppSettings] = None
|
|
|
|
# 설정 디렉토리 생성
|
|
self._settings_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
@property
|
|
def settings(self) -> AppSettings:
|
|
"""현재 설정 반환 (없으면 로드)"""
|
|
if self._settings is None:
|
|
self.load()
|
|
return self._settings
|
|
|
|
@property
|
|
def ai_settings(self) -> AISettings:
|
|
"""AI 설정 반환"""
|
|
return self.settings.ai
|
|
|
|
def load(self) -> AppSettings:
|
|
"""설정 파일에서 로드"""
|
|
app_data: Dict[str, Any] = {}
|
|
ai_data: Dict[str, Any] = {}
|
|
|
|
# 1) 앱 설정 로드
|
|
if self._app_settings_file.exists():
|
|
try:
|
|
with open(self._app_settings_file, "r", encoding="utf-8") as f:
|
|
app_data = json.load(f) or {}
|
|
except Exception as e:
|
|
print(f"앱 설정 로드 실패: {e}")
|
|
app_data = {}
|
|
|
|
# 2) AI 설정 로드 (별도 파일)
|
|
if self._ai_settings_file.exists():
|
|
try:
|
|
with open(self._ai_settings_file, "r", encoding="utf-8") as f:
|
|
ai_data = json.load(f) or {}
|
|
except Exception as e:
|
|
print(f"AI 설정 로드 실패: {e}")
|
|
ai_data = {}
|
|
else:
|
|
# 3) 마이그레이션: 과거 settings.json 안의 ai 섹션이 있으면 ai_settings.json로 이동
|
|
legacy_ai = (app_data or {}).get("ai")
|
|
if isinstance(legacy_ai, dict) and legacy_ai:
|
|
ai_data = legacy_ai
|
|
try:
|
|
# 저장 후 app_data에서 ai 섹션 제거
|
|
with open(self._ai_settings_file, "w", encoding="utf-8") as f:
|
|
json.dump(ai_data, f, indent=2, ensure_ascii=False)
|
|
app_data.pop("ai", None)
|
|
with open(self._app_settings_file, "w", encoding="utf-8") as f:
|
|
json.dump(app_data, f, indent=2, ensure_ascii=False)
|
|
except Exception as e:
|
|
print(f"AI 설정 마이그레이션 실패: {e}")
|
|
|
|
# AI 설정 복원
|
|
ai_settings = AISettings(
|
|
current_provider=ai_data.get("current_provider", "") if isinstance(ai_data, dict) else "",
|
|
providers=ai_data.get("providers", {}) if isinstance(ai_data, dict) else {},
|
|
)
|
|
|
|
self._settings = AppSettings(
|
|
ai=ai_settings,
|
|
theme=app_data.get("theme", "dark"),
|
|
language=app_data.get("language", "ko"),
|
|
window_geometry=app_data.get("window_geometry", {}),
|
|
recent_files=app_data.get("recent_files", []),
|
|
max_recent_files=app_data.get("max_recent_files", 10),
|
|
)
|
|
|
|
return self._settings
|
|
|
|
def save(self):
|
|
"""설정을 파일에 저장"""
|
|
if self._settings is None:
|
|
return
|
|
|
|
# 앱 설정 저장 (AI 제외)
|
|
try:
|
|
app_data = {
|
|
"theme": self._settings.theme,
|
|
"language": self._settings.language,
|
|
"window_geometry": self._settings.window_geometry,
|
|
"recent_files": self._settings.recent_files,
|
|
"max_recent_files": self._settings.max_recent_files,
|
|
}
|
|
with open(self._app_settings_file, "w", encoding="utf-8") as f:
|
|
json.dump(app_data, f, indent=2, ensure_ascii=False)
|
|
except Exception as e:
|
|
print(f"앱 설정 저장 실패: {e}")
|
|
|
|
# AI 설정 저장 (별도)
|
|
try:
|
|
ai_data = {
|
|
"current_provider": self._settings.ai.current_provider,
|
|
"providers": self._settings.ai.providers,
|
|
}
|
|
with open(self._ai_settings_file, "w", encoding="utf-8") as f:
|
|
json.dump(ai_data, f, indent=2, ensure_ascii=False)
|
|
except Exception as e:
|
|
print(f"AI 설정 저장 실패: {e}")
|
|
|
|
def reset(self):
|
|
"""설정 초기화"""
|
|
self._settings = AppSettings()
|
|
self.save()
|
|
|
|
def add_recent_file(self, file_path: str):
|
|
"""최근 파일 추가"""
|
|
if file_path in self.settings.recent_files:
|
|
self.settings.recent_files.remove(file_path)
|
|
|
|
self.settings.recent_files.insert(0, file_path)
|
|
|
|
# 최대 개수 제한
|
|
while len(self.settings.recent_files) > self.settings.max_recent_files:
|
|
self.settings.recent_files.pop()
|
|
|
|
self.save()
|
|
|
|
|
|
# 싱글톤 인스턴스
|
|
_settings_manager: Optional[SettingsManager] = None
|
|
|
|
|
|
def get_settings_manager() -> SettingsManager:
|
|
"""설정 관리자 싱글톤 인스턴스 반환"""
|
|
global _settings_manager
|
|
if _settings_manager is None:
|
|
_settings_manager = SettingsManager()
|
|
return _settings_manager
|
|
|
|
|
|
def get_settings() -> AppSettings:
|
|
"""현재 설정 반환"""
|
|
return get_settings_manager().settings
|
|
|
|
|
|
def get_ai_settings() -> AISettings:
|
|
"""AI 설정 반환"""
|
|
return get_settings_manager().ai_settings
|