feat(settings): support gateway source switching

This commit is contained in:
chengma
2026-07-20 16:29:28 +08:00
parent ed1d8ee764
commit 6a0cd1c763
18 changed files with 746 additions and 77 deletions
+11
View File
@@ -109,6 +109,9 @@ class MainWindow(QMainWindow):
self.tabs.currentChanged.connect(self._on_tab_changed)
for title in TAB_TITLES:
self.tabs.addTab(self._build_tab(title), title)
settings_tab = self._settings_tab()
if hasattr(settings_tab, "settingsSaved"):
settings_tab.settingsSaved.connect(self._on_settings_saved)
self.setCentralWidget(self.tabs)
self.show_status("就绪", level="muted")
if startup_status:
@@ -182,6 +185,14 @@ class MainWindow(QMainWindow):
if hasattr(widget, "refresh_tasks"):
widget.refresh_tasks()
def _on_settings_saved(self, backend):
for index in range(self.tabs.count()):
widget = self.tabs.widget(index)
if hasattr(widget, "refresh_gateway_state"):
widget.refresh_gateway_state()
label = "自定义网关" if str(backend) == "direct" else "默认网关"
self.show_status("设置已保存,当前使用%s" % label, level="success")
def _on_tab_changed(self, index):
if self._reverting_tab_change:
self._last_tab_index = index
+30 -5
View File
@@ -1089,7 +1089,7 @@ class GenerateTab(QWidget):
self.failed_progress_label = QLabel("失败 0")
self.failed_progress_label.setObjectName("generateFailedProgressLabel")
self.failed_progress_label.setVisible(False)
self.cmhub_balance_label = QLabel("cmhub余额:未获取")
self.cmhub_balance_label = QLabel("默认网关余额:未获取")
self.cmhub_balance_label.setObjectName("generateCmhubBalanceLabel")
self.cmhub_balance_label.setVisible(False)
self.title_elapsed_label = QLabel("生标题用时 0 秒")
@@ -1760,6 +1760,21 @@ class GenerateTab(QWidget):
if not self._save_generate_mode_setting(show_status=False):
return
generate_mode = self._current_generate_mode()
if not self._is_cmhub_backend():
try:
ai.validate_direct_generation_config(
self.config,
generate_mode,
models_path=(
self.config.get("ai_models_path")
or appconfig.ai_models_config_path(self.config)
),
)
except Exception as exc:
message = "自定义网关配置不完整:%s。请到⑤设置补齐本轮所需模型的地址、模型ID、API Key、接口类型和启用状态。" % str(exc)
QMessageBox.warning(self, "无法开始生成", message)
self._set_status("自定义网关配置不完整,请到⑤设置补齐", "warning")
return
generate_cover = appconfig.generate_mode_includes_cover(generate_mode)
base_candidates = self._generation_candidates(generate_mode)
if not base_candidates:
@@ -1837,7 +1852,12 @@ class GenerateTab(QWidget):
"generate_mode": generate_mode,
}
)
self._set_status(f"开始 AI 生成:{len(tasks)} 条")
if self._is_cmhub_backend():
self._set_status(f"开始 AI 生成:{len(tasks)} 条")
else:
self._set_status(
"开始 AI 生成:%d 条;自定义网关(不计点数,费用由服务商收取)" % len(tasks)
)
thread.start()
def _generation_candidates(self, generate_mode):
@@ -2282,7 +2302,7 @@ class GenerateTab(QWidget):
def _reset_cmhub_balance_label(self):
self.cmhub_balance_label.setVisible(False)
if self._is_cmhub_backend():
self.cmhub_balance_label.setText("cmhub余额:生成后刷新")
self.cmhub_balance_label.setText("默认网关余额:生成后刷新")
else:
self.cmhub_balance_label.setText("")
@@ -2293,9 +2313,14 @@ class GenerateTab(QWidget):
balance = payload.get("points_balance") if isinstance(payload, dict) else None
if balance is None:
if not self.cmhub_balance_label.text():
self.cmhub_balance_label.setText("cmhub余额:未获取")
self.cmhub_balance_label.setText("默认网关余额:未获取")
return
self.cmhub_balance_label.setText(f"cmhub余额:{balance}")
self.cmhub_balance_label.setText(f"默认网关余额:{balance}")
def refresh_gateway_state(self):
"""Clear source-specific feedback after ⑤ saves a new gateway source."""
self._reset_cmhub_balance_label()
def _is_cmhub_backend(self):
try:
+145 -6
View File
@@ -81,6 +81,7 @@ from ..workers import (
CMHubModelCatalogWorker,
ImageStudioDownloadOriginalWorker,
ImageStudioPullImagesWorker,
ImageStudioResumeJobsWorker,
ProductSuiteAiWriteWorker,
ProductSuiteGenerateWorker,
ProductSuiteHistoryExportWorker,
@@ -723,13 +724,20 @@ class SuiteResultCard(QFrame):
status_label.setWordWrap(False)
status_label.setToolTip(status_text)
footer.addWidget(status_label, 1)
self.retry_button = None
if status in {"failed", "expired", "cancelled"}:
retry_button = QPushButton("重试")
retry_button.setMinimumWidth(52)
retry_button.clicked.connect(lambda: self.retryRequested.emit(self.job))
footer.addWidget(retry_button)
self.retry_button = QPushButton("重试")
self.retry_button.setMinimumWidth(52)
self.retry_button.clicked.connect(lambda: self.retryRequested.emit(self.job))
footer.addWidget(self.retry_button)
layout.addLayout(footer)
def set_retry_enabled(self, enabled, tooltip=""):
if self.retry_button is None:
return
self.retry_button.setEnabled(bool(enabled))
self.retry_button.setToolTip(str(tooltip or ""))
class SuiteHistoryImageCard(QFrame):
"""Read-only image card used by the product-suite history dialog."""
@@ -1884,6 +1892,8 @@ class SuiteTaskState:
generation_price_worker: object = None
generation_price_thread: object = None
generation_confirmation_open: bool = False
resume_worker: object = None
resume_thread: object = None
download_queue: list = field(default_factory=list)
downloads: dict = field(default_factory=dict)
download_tokens: dict = field(default_factory=dict)
@@ -1962,6 +1972,10 @@ class ProductSuiteTab(QWidget):
if self._prompt_template_init_error:
self._status(self._prompt_template_init_error, "danger")
def showEvent(self, event):
super().showEvent(event)
self.refresh_gateway_state()
def _build_ui(self):
root = QVBoxLayout(self)
root.setContentsMargins(10, 8, 10, 8)
@@ -2111,6 +2125,11 @@ class ProductSuiteTab(QWidget):
"QPushButton:hover { background: #245fce; }"
)
layout.addWidget(self.generate_button)
self.resume_submitted_button = QPushButton("继续查询已提交图片")
self.resume_submitted_button.setObjectName("suiteResumeSubmittedButton")
self.resume_submitted_button.setToolTip("继续查询已提交到默认网关的图片,不会重新生成或再次扣点")
self.resume_submitted_button.setVisible(False)
layout.addWidget(self.resume_submitted_button)
self.generate_helper_label = QLabel("建议填写产品名称、核心卖点、目标人群、使用场景与禁用元素")
self.generate_helper_label.setWordWrap(True)
self.generate_helper_label.setStyleSheet("color: #6b7280;")
@@ -2396,6 +2415,7 @@ class ProductSuiteTab(QWidget):
self.custom_category_edit.returnPressed.connect(self._commit_custom_category)
self.custom_category_edit.editingFinished.connect(self._finish_custom_category_edit)
self.generate_button.clicked.connect(self.toggle_generation)
self.resume_submitted_button.clicked.connect(self.resume_submitted_jobs)
self.history_button.clicked.connect(self.open_history_dialog)
self.undo_button.clicked.connect(self.undo_delete)
self.more_button.clicked.connect(self._show_more_menu)
@@ -4155,7 +4175,105 @@ class ProductSuiteTab(QWidget):
if not state.generation_running():
self.generate_button.setText("生成套图(%d)" % total)
def _is_default_gateway(self):
try:
return appconfig.ai_backend(self.config) == "cmhub"
except Exception:
return False
@staticmethod
def _is_default_gateway_job(job):
return (
str(getattr(job, "generation_source", "") or "").strip().lower() == "cmhub"
and str(getattr(job, "provider", "") or "").strip().lower() == "cmhub"
and bool(str(getattr(job, "task_id", "") or "").strip())
)
def _resumable_default_gateway_jobs(self, state):
if state is None or state.project_id is None:
return []
try:
jobs = image_studio.list_resumable_jobs(
project_id=state.project_id,
include_failed_downloads=True,
path=self.db_path,
)
except Exception:
return []
return [job for job in jobs if self._is_default_gateway_job(job)]
def refresh_gateway_state(self):
state = self._displayed_state
if state is not None:
self._apply_running_state(state)
self._refresh_results(state)
def _update_resume_submitted_action(self, state):
jobs = self._resumable_default_gateway_jobs(state)
self.resume_submitted_button.setVisible(bool(jobs))
self.resume_submitted_button.setEnabled(
bool(jobs)
and state.resume_worker is None
and not state.generation_running()
and state.generation_price_worker is None
)
def resume_submitted_jobs(self, checked=False):
state = self._displayed_state
if state is None or state.project_id is None:
return
if state.resume_worker is not None:
self._status("正在继续查询已提交图片", "info")
return
jobs = self._resumable_default_gateway_jobs(state)
if not jobs:
self._status("当前没有可继续查询的默认网关图片", "info")
self._update_resume_submitted_action(state)
return
worker = ImageStudioResumeJobsWorker(
project_id=state.project_id,
aspect_ratio=state.settings.get("ratio", "1:1"),
db_path=self.db_path,
config=self.config,
cmhub_config_path=self.cmhub_config_path,
)
state.resume_worker = worker
worker.finished.connect(
lambda result, state=state: self._on_resume_submitted_finished(state, result)
)
worker.cancelled.connect(
lambda result, state=state: self._on_resume_submitted_finished(state, result)
)
state.resume_thread = self._start_thread(worker, "商品套图继续查询")
self._apply_running_state(state)
self._status("开始继续查询%d张已提交图片,不会重新生成或再次扣点" % len(jobs), "info")
def _on_resume_submitted_finished(self, state, result):
state.resume_worker = None
state.resume_thread = None
self._refresh_results(state)
self._update_resume_submitted_action(state)
if result.get("ok") is False:
error = str(result.get("error") or "")
if "cmhub_not_configured" in error or "缺少" in error:
self._message(
"继续查询失败",
"默认网关配置不可用,请恢复原默认网关配置后继续查询已提交图片。",
)
else:
self._message("继续查询失败", _user_error(error))
return
self._status("已完成已提交图片查询,请查看生成结果", "success")
def _require_default_gateway(self, action):
if self._is_default_gateway():
return True
self._message("当前不可用", "%s仅支持默认网关,请到⑤设置切换后再使用。" % action)
return False
def start_ai_write(self, checked=False):
if not self._require_default_gateway("商品套图AI帮写"):
return
state = self._displayed_state
if state is None:
return
@@ -4482,6 +4600,8 @@ class ProductSuiteTab(QWidget):
self.start_generation(state)
def start_generation(self, state, specs=None, *, retry_job_id=None):
if not self._require_default_gateway("商品套图生成"):
return False
if state.generation_running():
self._status("当前套图任务仍在生成", "warning")
return False
@@ -5414,6 +5534,7 @@ class ProductSuiteTab(QWidget):
generation_running = state.generation_running()
generation_price_pending = state.generation_price_worker is not None
pull_running = state.pull_running()
default_gateway = self._is_default_gateway()
self.pull_button.setText(
"正在停止..."
if pull_running and state.pull_stop_requested
@@ -5439,7 +5560,9 @@ class ProductSuiteTab(QWidget):
widget.setEnabled(not generation_running)
for row in self.category_rows.values():
row.set_controls_enabled(not generation_running)
self.generate_button.setEnabled(True)
self.generate_button.setEnabled(
generation_running or generation_price_pending or default_gateway
)
if generation_running:
self.generate_button.setText(
"正在停止..."
@@ -5465,8 +5588,17 @@ class ProductSuiteTab(QWidget):
)
self._refresh_totals(state)
ai_running = state.ai_worker is not None or state.ai_price_worker is not None
self.ai_write_button.setEnabled(not ai_running and not generation_running)
self.ai_write_button.setEnabled(
default_gateway and not ai_running and not generation_running
)
if not default_gateway and not ai_running:
self.ai_write_button.setToolTip("商品套图AI帮写仅支持默认网关")
self.generate_button.setToolTip("商品套图生成仅支持默认网关")
else:
self.ai_write_button.setToolTip("")
self.generate_button.setToolTip("")
self.ai_cancel_button.setVisible(ai_running)
self._update_resume_submitted_action(state)
self._update_context_actions(state)
def _refresh_elapsed(self):
@@ -5541,6 +5673,8 @@ class ProductSuiteTab(QWidget):
card.retryRequested.connect(self.retry_job)
card.menuRequested.connect(self._show_job_menu)
card.deleteRequested.connect(self.delete_job_asset)
if not self._is_default_gateway():
card.set_retry_enabled(False, "商品套图重新生成仅支持默认网关")
self.result_grid.addWidget(card, index // columns, index % columns)
self.result_summary_label.setText("共 %d 张 · 成功 %d 张" % (len(jobs), success))
self.undo_button.setVisible(bool(state.undo_records))
@@ -5611,6 +5745,8 @@ class ProductSuiteTab(QWidget):
ProductSuitePreviewDialog(asset.local_path, "%s预览" % job.job_type, self).exec()
def retry_job(self, job):
if not self._require_default_gateway("商品套图重新生成"):
return
state = self._displayed_state
if state is None:
return
@@ -5643,6 +5779,9 @@ class ProductSuiteTab(QWidget):
copy_action = menu.addAction("复制路径")
folder_action = menu.addAction("打开文件夹")
retry_action = menu.addAction("重新生成")
if not self._is_default_gateway():
retry_action.setEnabled(False)
retry_action.setToolTip("商品套图重新生成仅支持默认网关")
delete_action = menu.addAction("删除")
action = menu.exec(global_position)
asset = image_studio.get_asset(job.output_asset_id, path=self.db_path) if job.output_asset_id else None
+89 -24
View File
@@ -5,6 +5,8 @@ from __future__ import annotations
from contextlib import contextmanager
import os
from PySide6.QtCore import Signal
from ... import ai as ai_module
from ... import chrome
from ... import cmhub_models
@@ -14,7 +16,7 @@ from ..workers import CMHubSettingsWorker as _RealCMHubSettingsWorker
PLAINTEXT_CMHUB_API_KEY_WARNING = (
"cmhub API Key 会以本地明文保存到 data/config/cmhub.json,仅供本机调用 cmhub 网关使用。"
"默认网关 API Key 会以本地明文保存到 data/config/cmhub.json,仅供本机调用默认网关使用。"
"该文件已 gitignore,UI 打码显示,日志/导出不记录明文。"
)
@@ -29,7 +31,9 @@ def CMHubSettingsWorker(*args, **kwargs):
class SettingsTab(QWidget):
"""Tab 5: AI model definitions stored in data/config/ai_models.json."""
BACKEND_ITEMS = [("直连模型", "direct"), ("cmhub 网关", "cmhub")]
settingsSaved = Signal(str)
BACKEND_ITEMS = [("默认网关", "cmhub"), ("自定义网关", "direct")]
CATEGORY_ITEMS = [("文本", "text"), ("图像", "image")]
API_TYPE_ITEMS = [("chat", "chat"), ("images_edits", "images_edits"), ("auto", "auto")]
RESOLUTION_ITEMS = ["512", "1k", "2k", "4k"]
@@ -77,13 +81,37 @@ class SettingsTab(QWidget):
for label, value in self.BACKEND_ITEMS:
self.backend_combo.addItem(label, value)
self.backend_combo.setVisible(False)
self.gateway_default_button = QPushButton("默认网关")
self.gateway_default_button.setObjectName("gatewayDefaultButton")
self.gateway_default_button.setCheckable(True)
self.gateway_custom_button = QPushButton("自定义网关")
self.gateway_custom_button.setObjectName("gatewayCustomButton")
self.gateway_custom_button.setCheckable(True)
self.gateway_button_group = QButtonGroup(self)
self.gateway_button_group.setExclusive(True)
self.gateway_button_group.addButton(self.gateway_default_button)
self.gateway_button_group.addButton(self.gateway_custom_button)
self.gateway_selector = QWidget()
self.gateway_selector.setObjectName("gatewaySourceSelector")
gateway_selector_layout = QHBoxLayout(self.gateway_selector)
gateway_selector_layout.setContentsMargins(0, 0, 0, 0)
gateway_selector_layout.setSpacing(0)
gateway_selector_layout.addWidget(self.gateway_default_button)
gateway_selector_layout.addWidget(self.gateway_custom_button)
self.gateway_selector.setStyleSheet(
"QPushButton { min-width: 108px; padding: 6px 12px; border: 1px solid #b8c0ca; "
"background: #f6f8fa; color: #57606a; }"
"QPushButton:first-child { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }"
"QPushButton:last-child { border-left: 0; border-top-right-radius: 4px; border-bottom-right-radius: 4px; }"
"QPushButton:checked { background: #0969da; border-color: #0969da; color: white; font-weight: 600; }"
)
self.cmhub_base_url_edit = QLineEdit()
self.cmhub_base_url_edit.setObjectName("cmhubBaseUrlEdit")
self.cmhub_base_url_edit.setPlaceholderText("https://host(不要带 /api 或 /api/v1)")
self.cmhub_api_key_edit = QLineEdit()
self.cmhub_api_key_edit.setObjectName("cmhubApiKeyEdit")
self.cmhub_api_key_edit.setEchoMode(QLineEdit.Password)
self.cmhub_api_key_edit.setPlaceholderText("从 cmhub 网页端复制 API Key")
self.cmhub_api_key_edit.setPlaceholderText("从默认网关网页端复制 API Key")
self.cmhub_title_alias_combo = QComboBox()
self.cmhub_title_alias_combo.setObjectName("cmhubTitleAliasCombo")
self.cmhub_image_alias_combo = QComboBox()
@@ -103,10 +131,10 @@ class SettingsTab(QWidget):
self.cmhub_result_label = QLabel("")
self.cmhub_result_label.setObjectName("cmhubResultLabel")
self.cmhub_result_label.setWordWrap(True)
self.cmhub_base_url_hint_label = QLabel("Base URL 只填网关根,如 https://host;不要带 /api 或 /api/v1。")
self.cmhub_base_url_hint_label = QLabel("Base URL 只填默认网关根,如 https://host;不要带 /api 或 /api/v1。")
self.cmhub_base_url_hint_label.setObjectName("cmhubBaseUrlHintLabel")
self.cmhub_base_url_hint_label.setWordWrap(True)
self.cmhub_key_hint_label = QLabel("API Key 仅在 cmhub 网页端创建时显示一次;复制到此处后会本地明文保存并打码显示。")
self.cmhub_key_hint_label = QLabel("API Key 仅在默认网关网页端创建时显示一次;复制到此处后会本地明文保存并打码显示。")
self.cmhub_key_hint_label.setObjectName("cmhubKeyHintLabel")
self.cmhub_key_hint_label.setWordWrap(True)
@@ -235,7 +263,7 @@ class SettingsTab(QWidget):
("状态", self.enabled_checkbox),
("服务商名", self.name_edit),
("类别", self.category_combo),
("api_type", self.api_type_combo),
("接口类型", self.api_type_combo),
("模型ID", self.model_id_edit),
("连接超时(秒)", self.connect_timeout_spin),
("网址", self.url_edit, True),
@@ -296,8 +324,15 @@ class SettingsTab(QWidget):
model_detail_layout.addLayout(action_layout)
model_detail_layout.addWidget(self.test_result_label)
self.direct_gateway_notice = QLabel("图片理解与商品套图仅支持默认网关。")
self.direct_gateway_notice.setObjectName("directGatewayNoticeLabel")
self.direct_gateway_notice.setStyleSheet("color: #6b7280;")
self.direct_role_panel = QWidget()
self.direct_role_panel.setLayout(direct_role_form)
direct_role_layout = QVBoxLayout(self.direct_role_panel)
direct_role_layout.setContentsMargins(0, 0, 0, 0)
direct_role_layout.setSpacing(8)
direct_role_layout.addLayout(direct_role_form)
direct_role_layout.addWidget(self.direct_gateway_notice)
cmhub_form = self._three_column_form(
[
@@ -332,7 +367,7 @@ class SettingsTab(QWidget):
self.settings_panel_layout = panel_layout
panel_layout.setContentsMargins(13, 18, 13, 18)
self.ai_model_section_title = self._section_title(
"cmhub 网关",
"生成网关",
"settingsAiModelSectionTitle",
)
self.model_detail_section_title = self._section_title(
@@ -352,6 +387,8 @@ class SettingsTab(QWidget):
"settingsInfrastructureSectionTitle",
)
panel_layout.addWidget(self.ai_model_section_title)
panel_layout.addWidget(self.gateway_selector)
panel_layout.addSpacing(8)
panel_layout.addWidget(self.model_picker_panel)
panel_layout.addSpacing(14)
panel_layout.addWidget(self.model_detail_section_title)
@@ -394,7 +431,8 @@ class SettingsTab(QWidget):
self.delete_model_button.clicked.connect(self.delete_model)
self.save_model_button.clicked.connect(self.save_model)
self.test_connection_button.clicked.connect(self.test_connection)
self.backend_combo.currentIndexChanged.connect(self._on_backend_changed)
self.gateway_default_button.toggled.connect(self._on_gateway_source_toggled)
self.gateway_custom_button.toggled.connect(self._on_gateway_source_toggled)
self.cmhub_refresh_button.clicked.connect(self.refresh_cmhub_models)
self.cmhub_test_button.clicked.connect(self.test_cmhub_connection)
self.resolution_combo.currentIndexChanged.connect(
@@ -480,7 +518,6 @@ class SettingsTab(QWidget):
self.chrome_path_edit,
)
combos = (
self.backend_combo,
self.cmhub_title_alias_combo,
self.cmhub_image_alias_combo,
self.cmhub_vision_alias_combo,
@@ -516,6 +553,9 @@ class SettingsTab(QWidget):
for widget in checkboxes:
widget.toggled.connect(self._mark_dirty)
self.gateway_default_button.toggled.connect(self._mark_dirty)
self.gateway_custom_button.toggled.connect(self._mark_dirty)
def _mark_dirty(self, *args):
if self._suspend_dirty > 0:
return
@@ -576,13 +616,36 @@ class SettingsTab(QWidget):
self._cmhub_auto_refresh_done = True
self.refresh_cmhub_models()
def _selected_backend(self):
return "direct" if self.gateway_custom_button.isChecked() else "cmhub"
def _set_selected_backend(self, backend):
value = "direct" if str(backend or "").strip().lower() == "direct" else "cmhub"
target = self.gateway_custom_button if value == "direct" else self.gateway_default_button
for button in (self.gateway_default_button, self.gateway_custom_button):
previous = button.blockSignals(True)
button.setChecked(button is target)
button.blockSignals(previous)
previous = self.backend_combo.blockSignals(True)
self._set_combo_by_data(self.backend_combo, value)
self.backend_combo.blockSignals(previous)
def _on_gateway_source_toggled(self, checked):
if checked:
self._on_backend_changed()
def _on_backend_changed(self, index=None):
backend = self._selected_backend()
previous = self.backend_combo.blockSignals(True)
self._set_combo_by_data(self.backend_combo, backend)
self.backend_combo.blockSignals(previous)
self.backend_combo.setVisible(False)
self.model_picker_panel.setVisible(False)
self.model_detail_section_title.setVisible(False)
self.model_detail_panel.setVisible(False)
self.direct_role_panel.setVisible(False)
self.cmhub_panel.setVisible(True)
is_direct = backend == "direct"
self.model_picker_panel.setVisible(is_direct)
self.model_detail_section_title.setVisible(is_direct)
self.model_detail_panel.setVisible(is_direct)
self.direct_role_panel.setVisible(is_direct)
self.cmhub_panel.setVisible(not is_direct)
self._set_cmhub_running(self.cmhub_thread is not None)
self._update_button_state()
@@ -733,8 +796,10 @@ class SettingsTab(QWidget):
self._replace_config(saved)
self._populate_app_settings()
self._set_dirty(False)
self._set_status("设置已保存")
backend_label = "自定义网关" if self._selected_backend() == "direct" else "默认网关"
self._set_status("设置已保存,当前使用%s" % backend_label)
QMessageBox.information(self, "保存设置", "设置已保存")
self.settingsSaved.emit(self._selected_backend())
return True
def _app_settings_values(self):
@@ -748,7 +813,7 @@ class SettingsTab(QWidget):
self._show_error("默认调试端口必须在调试端口范围内")
return None
ai_cfg = appconfig.ai_config(self.config)
backend = "cmhub"
backend = self._selected_backend()
text_model = self.default_text_model_combo.currentData() or ai_cfg.get("default_text_model")
image_model = self.default_image_model_combo.currentData() or ai_cfg.get("default_image_model")
cmhub_cfg = self._cmhub_settings_values(backend)
@@ -847,7 +912,7 @@ class SettingsTab(QWidget):
return
self._populate_role_model_combos()
ai_cfg = appconfig.ai_config(self.config)
self._set_combo_by_data(self.backend_combo, "cmhub")
self._set_selected_backend(ai_cfg.get("backend", "cmhub"))
cmhub_cfg = appconfig.cmhub_config(self.config)
self.cmhub_base_url_edit.setText(appconfig.normalize_cmhub_base_url(cmhub_cfg.get("base_url", "")))
self._loaded_cmhub_api_key = appconfig.get_cmhub_api_key(path=self.cmhub_config_path)
@@ -1092,7 +1157,7 @@ class SettingsTab(QWidget):
def _start_cmhub_worker(self, include_balance):
if self.cmhub_thread is not None:
self._set_status("cmhub 检测正在进行...")
self._set_status("默认网关检测正在进行...")
return
base_url = appconfig.normalize_cmhub_base_url(self.cmhub_base_url_edit.text())
if base_url != self.cmhub_base_url_edit.text().strip():
@@ -1104,7 +1169,7 @@ class SettingsTab(QWidget):
if not api_key:
missing.append("API Key")
if missing:
self._show_error("cmhub 配置不完整:缺少 " + "、".join(missing))
self._show_error("默认网关配置不完整:缺少 " + "、".join(missing))
return
worker = CMHubSettingsWorker(
base_url,
@@ -1121,7 +1186,7 @@ class SettingsTab(QWidget):
self.cmhub_worker = worker
self.cmhub_thread = thread
self._set_cmhub_running(True)
message = "正在测试 cmhub 连接并查询余额..." if include_balance else "正在刷新 cmhub 别名..."
message = "正在测试默认网关连接并查询余额..." if include_balance else "正在刷新默认网关别名..."
self.cmhub_result_label.setText(message)
self._set_status(message)
thread.start()
@@ -1183,8 +1248,8 @@ class SettingsTab(QWidget):
def _cmhub_success_subject(self, payload):
account_name = self._cmhub_account_display_name(payload)
if account_name:
return f"cmhub 账号「{account_name}」连接成功"
return "cmhub 连接成功"
return f"账号「{account_name}」连接默认网关成功"
return "默认网关连接成功"
def _cmhub_account_display_name(self, payload):
if not isinstance(payload, dict):
@@ -1237,7 +1302,7 @@ class SettingsTab(QWidget):
return ""
def _on_cmhub_failed(self, _task_id, error):
message = f"cmhub 连接失败:{error}"
message = "默认网关连接失败:%s" % str(error or "连接失败").replace("cmhub", "默认网关")
self.cmhub_result_label.setText(message)
self._set_status(message)
+39 -5
View File
@@ -285,7 +285,13 @@ class ImageStudioGenerateJobsWorker(BaseWorker):
self.job_type = str(job_type or "main")
self.aspect_ratio = str(aspect_ratio or "1:1")
self.db_path = db_path
self.config = config
self.config = ai.freeze_runtime_config(
config,
cmhub_config_path=cmhub_config_path,
models_path=(config or {}).get("ai_models_path", appconfig.AI_MODELS_PATH),
include_cmhub=True,
include_direct_models=False,
)
self.cmhub_config_path = cmhub_config_path
self._done = 0
self._failed = 0
@@ -358,7 +364,13 @@ class ProductSuiteGenerateWorker(BaseWorker):
self.generation_round_key = str(generation_round_key or "").strip()
self.aspect_ratio = str(aspect_ratio or "1:1")
self.db_path = db_path
self.config = config
self.config = ai.freeze_runtime_config(
config,
cmhub_config_path=cmhub_config_path,
models_path=(config or {}).get("ai_models_path", appconfig.AI_MODELS_PATH),
include_cmhub=True,
include_direct_models=False,
)
self.cmhub_config_path = cmhub_config_path
self.job_ids = []
self._done = 0
@@ -366,6 +378,8 @@ class ProductSuiteGenerateWorker(BaseWorker):
self._lock = threading.Lock()
def execute(self):
if appconfig.ai_backend(self.config) != "cmhub":
raise ValueError("商品套图仅支持默认网关,请到⑤设置切换后再生成")
total = len(self.job_specs)
if total <= 0:
raise ValueError("商品套图生成任务不能为空")
@@ -474,12 +488,20 @@ class ProductSuiteAiWriteWorker(BaseWorker):
self.instruction = str(instruction or "")
self.context = str(context or "")
self.image_paths = [str(path or "") for path in list(image_paths or [])]
self.config = config
self.config = ai.freeze_runtime_config(
config,
cmhub_config_path=cmhub_config_path,
models_path=(config or {}).get("ai_models_path", appconfig.AI_MODELS_PATH),
include_cmhub=True,
include_direct_models=False,
)
self.cmhub_config_path = cmhub_config_path
def execute(self):
if self.should_cancel():
return {"cancelled": True}
if appconfig.ai_backend(self.config) != "cmhub":
raise ValueError("商品套图AI帮写仅支持默认网关,请到⑤设置切换后再使用")
result = ai.analyze_product_images(
self.instruction,
self.context,
@@ -583,7 +605,13 @@ class ImageStudioResumeJobsWorker(BaseWorker):
self.project_id = int(project_id) if project_id is not None else None
self.aspect_ratio = str(aspect_ratio or "1:1")
self.db_path = db_path
self.config = config
self.config = ai.freeze_runtime_config(
config,
cmhub_config_path=cmhub_config_path,
models_path=(config or {}).get("ai_models_path", appconfig.AI_MODELS_PATH),
include_cmhub=True,
include_direct_models=False,
)
self.cmhub_config_path = cmhub_config_path
self._done = 0
self._failed = 0
@@ -689,7 +717,11 @@ class GenerateWorker(BaseWorker):
self.tasks = list(tasks)
self.prompt_values = dict(prompt_values or {})
self.db_path = db_path
self.config = config
self.config = ai.freeze_runtime_config(
config,
cmhub_config_path=(config or {}).get("cmhub_config_path", appconfig.CMHUB_CONFIG_PATH),
models_path=(config or {}).get("ai_models_path", appconfig.AI_MODELS_PATH),
)
self.diagnostic_log_dir = diagnostic_log_dir
self.generation_scope = product_status.normalize_scope(generation_scope)
self.product_status_counts = {
@@ -786,6 +818,8 @@ class GenerateWorker(BaseWorker):
),
excluded=self.status_scope_excluded,
)
if appconfig.ai_backend(self.config) == "direct":
start_message += ";自定义网关(不计点数,费用由服务商收取)"
self._log_run_event(start_message)
try:
summary = ai.generate_batch(