diff --git a/src-python/config.py b/src-python/config.py index 3a3e7c3b..55a46122 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -527,6 +527,19 @@ def _compute_device_validator(val, inst): return copy.deepcopy(val) return None +def _allowed_in_populated(list_attr_name: str): + def _inner(value, inst): + try: + lst = getattr(inst, list_attr_name) + except Exception: + return True # インスタンス状態取得失敗時も弾かない + if not lst: # 空/未初期化 + return True + if value is None: + return True + return value in lst + return _inner + class Config: """Application configuration singleton. @@ -558,8 +571,16 @@ class Config: return cls._instance def saveConfigToFile(self) -> None: + # 永続化対象を descriptor 情報 (json_serializable_vars) から再構成 + filtered = {} + for var_name, var_func in json_serializable_vars.items(): + try: + filtered[var_name] = var_func(self) + except Exception: + pass + self._config_data = filtered with open(self.PATH_CONFIG, "w", encoding="utf-8") as fp: - json_dump(self._config_data, fp, indent=4, ensure_ascii=False) + json_dump(filtered, fp, indent=4, ensure_ascii=False) def saveConfig(self, key: str, value: Any, immediate_save: bool = False) -> None: self._config_data[key] = value @@ -601,12 +622,12 @@ class Config: # Read Write # --- Simple boolean flags (managed by descriptor) --- - ENABLE_TRANSLATION = ManagedProperty('ENABLE_TRANSLATION', type_=bool) - ENABLE_TRANSCRIPTION_SEND = ManagedProperty('ENABLE_TRANSCRIPTION_SEND', type_=bool) - ENABLE_TRANSCRIPTION_RECEIVE = ManagedProperty('ENABLE_TRANSCRIPTION_RECEIVE', type_=bool) - ENABLE_FOREGROUND = ManagedProperty('ENABLE_FOREGROUND', type_=bool) - ENABLE_CHECK_ENERGY_SEND = ManagedProperty('ENABLE_CHECK_ENERGY_SEND', type_=bool) - ENABLE_CHECK_ENERGY_RECEIVE = ManagedProperty('ENABLE_CHECK_ENERGY_RECEIVE', type_=bool) + ENABLE_TRANSLATION = ManagedProperty('ENABLE_TRANSLATION', type_=bool, serialize=False) + ENABLE_TRANSCRIPTION_SEND = ManagedProperty('ENABLE_TRANSCRIPTION_SEND', type_=bool, serialize=False) + ENABLE_TRANSCRIPTION_RECEIVE = ManagedProperty('ENABLE_TRANSCRIPTION_RECEIVE', type_=bool, serialize=False) + ENABLE_FOREGROUND = ManagedProperty('ENABLE_FOREGROUND', type_=bool, serialize=False) + ENABLE_CHECK_ENERGY_SEND = ManagedProperty('ENABLE_CHECK_ENERGY_SEND', type_=bool, serialize=False) + ENABLE_CHECK_ENERGY_RECEIVE = ManagedProperty('ENABLE_CHECK_ENERGY_RECEIVE', type_=bool, serialize=False) # --- Selectable dict/list properties (managed by descriptor, not serialized) --- # These are dynamically generated in init_config() based on installed packages/APIs @@ -711,11 +732,11 @@ class Config: USE_EXCLUDE_WORDS = ManagedProperty('USE_EXCLUDE_WORDS', type_=bool) CTRANSLATE2_WEIGHT_TYPE = ManagedProperty('CTRANSLATE2_WEIGHT_TYPE', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_LIST) WHISPER_WEIGHT_TYPE = ManagedProperty('WHISPER_WEIGHT_TYPE', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_WHISPER_WEIGHT_TYPE_LIST) - SELECTED_PLAMO_MODEL = ManagedProperty('SELECTED_PLAMO_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_PLAMO_MODEL_LIST) - SELECTED_GEMINI_MODEL = ManagedProperty('SELECTED_GEMINI_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_GEMINI_MODEL_LIST) - SELECTED_OPENAI_MODEL = ManagedProperty('SELECTED_OPENAI_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_OPENAI_MODEL_LIST) - SELECTED_LMSTUDIO_MODEL = ManagedProperty('SELECTED_LMSTUDIO_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_LMSTUDIO_MODEL_LIST) - SELECTED_OLLAMA_MODEL = ManagedProperty('SELECTED_OLLAMA_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_OLLAMA_MODEL_LIST) + SELECTED_PLAMO_MODEL = ManagedProperty('SELECTED_PLAMO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_PLAMO_MODEL_LIST')) + SELECTED_GEMINI_MODEL = ManagedProperty('SELECTED_GEMINI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_GEMINI_MODEL_LIST')) + SELECTED_OPENAI_MODEL = ManagedProperty('SELECTED_OPENAI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OPENAI_MODEL_LIST')) + SELECTED_LMSTUDIO_MODEL = ManagedProperty('SELECTED_LMSTUDIO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_LMSTUDIO_MODEL_LIST')) + SELECTED_OLLAMA_MODEL = ManagedProperty('SELECTED_OLLAMA_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OLLAMA_MODEL_LIST')) # --- Translation and language settings --- MIC_WORD_FILTER = ValidatedProperty('MIC_WORD_FILTER', _mic_word_filter_validator) @@ -1006,15 +1027,44 @@ class Config: self._config_data = json_load(fp) for key, value in self._config_data.items(): + # 読み込み時: serialize=True かつ readonlyでない Descriptor のみ反映。 + # 未知キー(Descriptorなし)は無視して注入を防止。 try: - setattr(self, key, value) + descriptor = getattr(type(self), key, None) + if isinstance(descriptor, ManagedProperty): + if descriptor.readonly or not descriptor.serialize: + continue + setattr(self, key, value) + elif isinstance(descriptor, ValidatedProperty): + if not descriptor.serialize: + continue + setattr(self, key, value) + else: + # 不明キーは破棄(古い/不要/改竄の可能性) + continue except Exception: errorLogging() + self.saveConfigToFile() - with open(self.PATH_CONFIG, 'w', encoding="utf-8") as fp: - for var_name, var_func in json_serializable_vars.items(): - self._config_data[var_name] = var_func(self) - json_dump(self._config_data, fp, indent=4, ensure_ascii=False) + def revalidate_selected_models(self): + pairs = [ + ('SELECTED_PLAMO_MODEL', 'SELECTABLE_PLAMO_MODEL_LIST'), + ('SELECTED_GEMINI_MODEL', 'SELECTABLE_GEMINI_MODEL_LIST'), + ('SELECTED_OPENAI_MODEL', 'SELECTABLE_OPENAI_MODEL_LIST'), + ('SELECTED_LMSTUDIO_MODEL', 'SELECTABLE_LMSTUDIO_MODEL_LIST'), + ('SELECTED_OLLAMA_MODEL', 'SELECTABLE_OLLAMA_MODEL_LIST'), + ] + for sel_attr, list_attr in pairs: + try: + current = getattr(self, sel_attr) + lst = getattr(self, list_attr) + if lst and current is not None and current not in lst: + if len(lst) > 0: + setattr(self, sel_attr, lst[0]) + else: + setattr(self, sel_attr, None) + except Exception: + errorLogging() # Auto-register all descriptors after Config class definition _auto_register_descriptors() diff --git a/src-python/controller.py b/src-python/controller.py index db326cb2..2f3aa530 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -3138,6 +3138,9 @@ class Controller: model.stopWebSocketServer() printLog("WebSocket server host or port is not available") + printLog("Revalidate Selected Models") + config.revalidate_selected_models() + printLog("Update settings") self.updateConfigSettings()