refactor(ai-outfit): 标题模型改为「配置定名」、去掉下拉 (§19.19)
标题模型是「配一次就固定」的,不像分辨率/话术需每次选;左栏 ~360px 已叠两个 提示词组,去掉下拉更干净,还消掉「误选图片模型」的坑。模型名放 app_config.title_model (默认 GPT-5.5 文本),运行时按名字查 ai_models.json —— 换模型只改配置不改代码(docs/11 §17.3/§17.6)。 - ai_outfit_panel.py:_build_title_group 去掉标题模型下拉;apply_config 改记 title_model 名字、_emit_config 不再写(UI 不覆盖);_selected_title_model_config → 纯函数 _find_title_model(按 name 查 + 校验,返回 (config, error))+ UI 包装 _resolve_title_model_config;找不到/未配置/图片接口时明确报错 - config_service:DEFAULT_CONFIG.title_model 默认改为 "GPT-5.5 文本" - docs/ai_models.sample.json:补一条 name=「GPT-5.5 文本」的 chat 文本模型样板(占位 key) - 测试:面板断言无标题模型下拉;_find_title_model 命中/缺失/无模型三例;全套 py37 通过 - 冒烟:seeded ai_models.json + apply_config → title_model 名字解析到 gpt-5.5(chat),缺失→报错 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -234,6 +234,7 @@ class AiOutfitPanel(QWidget):
|
||||
self._title_thread = None # 标题生成线程(§17)
|
||||
self._title_worker = None
|
||||
self._title_fail = 0 # running failure count for title stats
|
||||
self._title_model_name = "" # configured 标题模型 name (§17.3 配置定名)
|
||||
self._row_to_table = {} # excel row_index -> table row
|
||||
self._failures = [] # list of OutfitResult (failed)
|
||||
self._last_resolution = "" # for "value actually changed" check (§10.2)
|
||||
@@ -373,7 +374,10 @@ class AiOutfitPanel(QWidget):
|
||||
return scroll
|
||||
|
||||
def _build_title_group(self):
|
||||
"""标题生成组(§17):提示词 + 标题模型下拉 + 保存 + 生成标题。"""
|
||||
"""标题生成组(§17):提示词 + 保存 + 生成标题。
|
||||
|
||||
无标题模型下拉——模型由 app_config.title_model「配置定名」(§17.3/§17.6)。
|
||||
"""
|
||||
box = QGroupBox()
|
||||
v = QVBoxLayout(box)
|
||||
v.addWidget(QLabel("标题生成提示词"))
|
||||
@@ -381,13 +385,6 @@ class AiOutfitPanel(QWidget):
|
||||
self._title_prompt_edit.setMinimumHeight(110)
|
||||
v.addWidget(self._title_prompt_edit)
|
||||
|
||||
model_row = QHBoxLayout()
|
||||
model_row.addWidget(QLabel("标题模型"))
|
||||
self._title_model_combo = QComboBox()
|
||||
self._compact_combo(self._title_model_combo)
|
||||
model_row.addWidget(self._title_model_combo, stretch=1)
|
||||
v.addLayout(model_row)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
save_title_btn = QPushButton("保存")
|
||||
save_title_btn.clicked.connect(self._save_title_prompt)
|
||||
@@ -582,10 +579,11 @@ class AiOutfitPanel(QWidget):
|
||||
|
||||
# 标题生成提示词(单份,§17.3)
|
||||
self._title_prompt_edit.setPlainText(load_title_prompt())
|
||||
# 标题模型「配置定名」:记下名字,运行时按名查 ai_models.json(§17.3,无下拉)
|
||||
self._title_model_name = str(config.get("title_model", "") or "")
|
||||
|
||||
self._models = load_ai_models()
|
||||
self._fill_model_combo(self._model_combo, config.get("outfit_model", ""))
|
||||
self._fill_model_combo(self._title_model_combo, config.get("title_model", ""))
|
||||
|
||||
# Snapshot current dropdown values so a later user re-select of the same
|
||||
# item doesn't trigger the info popup (§10.2).
|
||||
@@ -622,7 +620,7 @@ class AiOutfitPanel(QWidget):
|
||||
"outfit_quality": self._quality.currentText(),
|
||||
"outfit_retry_failed": self._retry_failed_chk.isChecked(),
|
||||
"outfit_prompt_name": self._current_prompt_name,
|
||||
"title_model": self._title_model_combo.currentText() if self._models else "",
|
||||
# title_model 由 app_config 配置定名(§17.3),UI 不写、不覆盖。
|
||||
})
|
||||
|
||||
# -- left actions ---------------------------------------------------
|
||||
@@ -786,23 +784,34 @@ class AiOutfitPanel(QWidget):
|
||||
if not silent:
|
||||
self.statusBar_message("标题提示词已保存")
|
||||
|
||||
def _selected_title_model_config(self):
|
||||
def _find_title_model(self):
|
||||
"""Resolve the configured 标题模型 → (AiModelConfig|None, error|None). Pure.
|
||||
|
||||
Looks up app_config.title_model (a name) in ai_models.json by 'name'
|
||||
(matching how models are labelled), so 换模型 is a config edit (§17.3).
|
||||
"""
|
||||
if not self._models:
|
||||
QMessageBox.warning(
|
||||
self, "未配置模型",
|
||||
"尚未配置 AI 模型。请在 ~/.cmbot/config/ai_models.json 添加后重试。")
|
||||
return None
|
||||
idx = self._title_model_combo.currentIndex()
|
||||
if idx < 0 or idx >= len(self._models):
|
||||
QMessageBox.warning(self, "未选择标题模型", "请先选择标题模型。")
|
||||
return None
|
||||
return None, ("尚未配置 AI 模型。请在 ~/.cmbot/config/ai_models.json "
|
||||
"添加可生成文字的模型(chat/gemini)后重试。")
|
||||
name = (self._title_model_name or "").strip()
|
||||
data = next((m for m in self._models
|
||||
if (m.get("name") or m.get("model")) == name), None)
|
||||
if data is None:
|
||||
return None, ("未找到标题模型「{}」。请在 ai_models.json 添加可生成文字的"
|
||||
"模型(chat/gemini),并把 app_config 的 title_model 设为它的 "
|
||||
"name。".format(name or "(未配置)"))
|
||||
from services.ai_image_service import AiModelConfig, api_config_errors
|
||||
data = self._models[idx]
|
||||
errors = api_config_errors(data)
|
||||
if errors:
|
||||
QMessageBox.warning(self, "模型配置有误", ";".join(errors))
|
||||
return None, "标题模型「{}」配置有误:{}".format(name, ";".join(errors))
|
||||
return AiModelConfig.from_dict(data), None
|
||||
|
||||
def _resolve_title_model_config(self):
|
||||
config, error = self._find_title_model()
|
||||
if error:
|
||||
QMessageBox.warning(self, "标题模型不可用", error)
|
||||
return None
|
||||
return AiModelConfig.from_dict(data)
|
||||
return config
|
||||
|
||||
def _start_title(self):
|
||||
if self._thread is not None or self._title_thread is not None:
|
||||
@@ -811,7 +820,7 @@ class AiOutfitPanel(QWidget):
|
||||
if not excel:
|
||||
QMessageBox.information(self, "提示", "请先选择 Excel 文件。")
|
||||
return
|
||||
model_config = self._selected_title_model_config()
|
||||
model_config = self._resolve_title_model_config()
|
||||
if model_config is None:
|
||||
return
|
||||
prompt = self._title_prompt_edit.toPlainText().strip()
|
||||
@@ -905,7 +914,6 @@ class AiOutfitPanel(QWidget):
|
||||
self._title_btn.setText("生成中…" if running else "生成标题")
|
||||
self._start_btn.setEnabled(not running) # mutually exclusive with 开始生成
|
||||
self._excel_edit.setEnabled(not running)
|
||||
self._title_model_combo.setEnabled(not running and bool(self._models))
|
||||
|
||||
# -- switch info popups (user-only; §10.2) --------------------------
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ DEFAULT_CONFIG = {
|
||||
"outfit_quality": "均衡",
|
||||
"outfit_retry_failed": False,
|
||||
"outfit_prompt_name": "默认", # last-selected 话术模板 name (docs/11 §7.2)
|
||||
"title_model": "", # last-selected 标题模型 name (docs/11 §17.3)
|
||||
"title_model": "GPT-5.5 文本", # 标题模型名字(对应 ai_models.json 的 name,§17.3 配置定名)
|
||||
}
|
||||
|
||||
_CONFIG_FILENAME = "app_config.json"
|
||||
|
||||
Reference in New Issue
Block a user