测试修改

This commit is contained in:
DXC
2026-06-25 18:11:50 +08:00
parent d327c6c267
commit 429ed3c1c1
3 changed files with 83 additions and 72 deletions

View File

@ -165,24 +165,43 @@ class BandConfirmDialog(QDialog):
from PyQt5.QtCore import QSettings
AI_SETTINGS_ORG = "IrisWaterQuality"
AI_SETTINGS_APP = "WQ_GUI"
# 扩充预设字典,覆盖市面主流大模型标准接口
AI_DEFAULTS = {
"ollama": {
"api_base_url": "http://localhost:11434",
"vision_model": "qwen3-vl:8b",
"text_model": "qwen3-vl:8b",
"aliyun": {
"api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
"vision_model": "qwen-vl-max",
"text_model": "qwen-max",
},
"zhipu": {
"api_base_url": "https://open.bigmodel.cn/api/paas/v4/chat/completions",
"vision_model": "glm-4v",
"text_model": "glm-4",
},
"deepseek": {
"api_base_url": "https://api.deepseek.com/chat/completions",
"vision_model": "deepseek-chat", # DeepSeek暂无独立视觉API可用通用或自行更换
"text_model": "deepseek-chat",
},
"openai": {
"api_base_url": "https://api.openai.com/v1/chat/completions",
"vision_model": "gpt-4o",
"text_model": "gpt-4o",
},
"minimax": {
"api_base_url": "https://api.minimaxi.com/v1/text/chatcompletion_v2",
"vision_model": "abab6.5s-chat",
"api_base_url": "https://api.minimax.chat/v1/chat/completions",
"vision_model": "abab6.5g-chat",
"text_model": "abab6.5s-chat",
},
"ollama": {
"api_base_url": "http://localhost:11434",
"vision_model": "qwen2-vl",
"text_model": "qwen2.5",
},
}
class AISettingsDialog(QDialog):
"""AI 引擎可视化配置弹窗,配置持久化到 QSettings。"""
@ -195,41 +214,22 @@ class AISettingsDialog(QDialog):
self._init_ui()
def _load_settings(self):
"""从 QSettings 读取已有配置;无记录则回退到环境变量或默认值"""
"""从 QSettings 读取已有配置;无记录则回退到预设字典或环境变量。"""
s = QSettings(AI_SETTINGS_ORG, AI_SETTINGS_APP)
self._provider = s.value("ai_provider", "minimax", type=str)
# 默认推荐 Aliyun (通义千问)
self._provider = s.value("ai_provider", "Aliyun", type=str)
# API Key 不设默认值(敏感信息,首次必须由用户输入)
self._api_key = s.value("minimax_api_key", "", type=str)
# 【向后兼容补丁】优先读取新规范的 api_key如果为空尝试读取旧版本留下的 minimax_api_key
self._api_key = s.value("api_key", "", type=str)
if not self._api_key:
self._api_key = s.value("minimax_api_key", "", type=str)
# 已保存的 URL 和模型;若 QSettings 无记录则读环境变量
if self._provider == "ollama":
self._api_base_url = (
s.value("api_base_url", "")
or os.environ.get("OLLAMA_URL", AI_DEFAULTS["ollama"]["api_base_url"])
)
self._vision_model = (
s.value("vision_model", "")
or os.environ.get("OLLAMA_VISION_MODEL", AI_DEFAULTS["ollama"]["vision_model"])
)
self._text_model = (
s.value("text_model", "")
or os.environ.get("OLLAMA_TEXT_MODEL", AI_DEFAULTS["ollama"]["text_model"])
)
else:
self._api_base_url = (
s.value("api_base_url", "")
or os.environ.get("MINIMAX_BASE_URL", AI_DEFAULTS["minimax"]["api_base_url"])
)
self._vision_model = (
s.value("vision_model", "")
or os.environ.get("MINIMAX_VISION_MODEL", AI_DEFAULTS["minimax"]["vision_model"])
)
self._text_model = (
s.value("text_model", "")
or os.environ.get("MINIMAX_TEXT_MODEL", AI_DEFAULTS["minimax"]["text_model"])
)
provider_key = self._provider.lower()
defaults = AI_DEFAULTS.get(provider_key, {"api_base_url": "", "vision_model": "", "text_model": ""})
self._api_base_url = s.value("api_base_url", "", type=str) or defaults.get("api_base_url", "")
self._vision_model = s.value("vision_model", "", type=str) or defaults.get("vision_model", "")
self._text_model = s.value("text_model", "", type=str) or defaults.get("text_model", "")
self._timeout = s.value("timeout_s", 120, type=int)
def _init_ui(self):
@ -240,9 +240,15 @@ class AISettingsDialog(QDialog):
provider_row = QHBoxLayout()
provider_row.addWidget(QLabel("AI 引擎提供商:"))
self._provider_combo = QComboBox()
self._provider_combo.addItems(["Ollama", "Minimax"])
self._provider_combo.setCurrentText("Ollama" if self._provider == "ollama" else "Minimax")
self._provider_combo.currentIndexChanged.connect(self._on_provider_changed)
# ★ 核心改动:开启可编辑模式,允许用户随意输入第三方代理商名字
self._provider_combo.setEditable(True)
self._provider_combo.addItems(["Aliyun", "Zhipu", "DeepSeek", "OpenAI", "Minimax", "Ollama"])
self._provider_combo.setCurrentText(self._provider)
# 当文本改变时自动带出推荐配置
self._provider_combo.currentTextChanged.connect(self._on_provider_changed)
provider_row.addWidget(self._provider_combo, 1)
provider_row.addStretch(1)
layout.addLayout(provider_row)
@ -251,7 +257,7 @@ class AISettingsDialog(QDialog):
url_row = QHBoxLayout()
url_row.addWidget(QLabel("API Base URL:"))
self._url_edit = QLineEdit(self._api_base_url)
self._url_edit.setPlaceholderText("例如: http://localhost:11434")
self._url_edit.setPlaceholderText("填入兼容 OpenAI 规范的完整 URL")
url_row.addWidget(self._url_edit, 1)
layout.addLayout(url_row)
@ -259,7 +265,7 @@ class AISettingsDialog(QDialog):
key_row = QHBoxLayout()
key_row.addWidget(QLabel("API Key:"))
self._key_edit = QLineEdit(self._api_key)
self._key_edit.setPlaceholderText("输入 API Key敏感信息,已加密存储)")
self._key_edit.setPlaceholderText("输入 API Key本地加密存储)")
self._key_edit.setEchoMode(QLineEdit.Password)
key_row.addWidget(self._key_edit, 1)
layout.addLayout(key_row)
@ -288,10 +294,10 @@ class AISettingsDialog(QDialog):
# ── 说明 ──────────────────────────────────────────────────────────────
hint = QLabel(
"提示:切换引擎后将自动填充推荐默认值(可手动修改)。"
"API Key 仅本地加密存储,不会明文暴露"
"提示:可以直接在下拉框输入任意名称。选择预设服务商会自动填充推荐的兼容接口 URL。\n"
"若使用全能多模态大模型(如 gpt-4o / qwen-vl-max 等),视觉与文本模型填入相同名称即可"
)
hint.setStyleSheet("color: #888; font-size: 10px;")
hint.setStyleSheet("color: #888; font-size: 11px;")
hint.setWordWrap(True)
layout.addWidget(hint)
@ -306,18 +312,20 @@ class AISettingsDialog(QDialog):
btn_box.addButton(cancel_btn, QDialogButtonBox.RejectRole)
layout.addWidget(btn_box)
def _on_provider_changed(self):
"""切换 Provider 时自动填充推荐默认值。"""
provider = self._provider_combo.currentText().lower()
defaults = AI_DEFAULTS.get(provider, AI_DEFAULTS["minimax"])
self._url_edit.setText(defaults["api_base_url"])
self._vision_edit.setText(defaults["vision_model"])
self._text_edit.setText(defaults["text_model"])
def _on_provider_changed(self, text):
"""切换或输入 Provider 时自动填充推荐默认值。"""
provider_key = text.lower()
if provider_key in AI_DEFAULTS:
defaults = AI_DEFAULTS[provider_key]
self._url_edit.setText(defaults["api_base_url"])
self._vision_edit.setText(defaults["vision_model"])
self._text_edit.setText(defaults["text_model"])
def _save_and_close(self):
"""持久化到 QSettings 并关闭。"""
s = QSettings(AI_SETTINGS_ORG, AI_SETTINGS_APP)
provider = self._provider_combo.currentText().lower()
# 获取用户输入的文本(无论是选的还是自己打字的)
provider = self._provider_combo.currentText().strip()
s.setValue("ai_provider", provider)
s.setValue("api_base_url", self._url_edit.text().strip())
s.setValue("api_key", self._key_edit.text().strip())
@ -334,11 +342,13 @@ class AISettingsDialog(QDialog):
返回键ai_provider / api_base_url / api_key / vision_model / text_model / timeout_s
"""
s = QSettings(AI_SETTINGS_ORG, AI_SETTINGS_APP)
provider = s.value("ai_provider", "minimax", type=str)
# 默认返回 Aliyun
provider = s.value("ai_provider", "Aliyun", type=str)
return {
"ai_provider": provider,
"api_base_url": s.value("api_base_url", "", type=str),
"api_key": s.value("api_key", "", type=str),
# 同样向后兼容读取旧版本的 key
"api_key": s.value("api_key", s.value("minimax_api_key", "", type=str), type=str),
"vision_model": s.value("vision_model", "", type=str),
"text_model": s.value("text_model", "", type=str),
"timeout_s": s.value("timeout_s", 120, type=int),

View File

@ -64,8 +64,9 @@ class ReportWorkerThread(QThread):
)
else:
ai_cfg = ReportGenerationConfig(
ai_provider="minimax",
ai_provider=provider,
minimax_api_key=s.value("api_key", "", type=str) or "",
minimax_base_url=s.value("api_base_url", "", type=str) or None, # <--- 新增这行,把界面上的 URL 传过去
minimax_vision_model=s.value("vision_model", "", type=str) or None,
minimax_text_model=s.value("text_model", "", type=str) or None,
minimax_timeout_s=timeout,

View File

@ -91,6 +91,7 @@ class ReportGenerationConfig:
self.on_step = on_step
self.n = 0
self._render()
@ -164,8 +165,11 @@ class WaterQualityReportGenerator:
# 通用
ai_provider: Optional[str] = None # "ollama" | "minimax",默认 "minimax"
ai_provider: Optional[str] = None # "ollama" | "minimax",默认 "minimax"
enable_ai_analysis: Optional[bool] = None
# Ollama 专属
ollama_base_url: Optional[str] = None
@ -332,8 +336,6 @@ class WaterQualityReportGenerator:
self.minimax_base_url = (
cfg.minimax_base_url.rstrip("/")
)
if cfg and getattr(cfg, 'minimax_base_url', None)
@ -382,11 +384,11 @@ class WaterQualityReportGenerator:
else:
self.enable_ai_analysis = os.environ.get("ENABLE_AI_ANALYSIS", "1") not in {
"0",
"false",
"False",
}
@ -399,8 +401,6 @@ class WaterQualityReportGenerator:
self.parameter_descriptions = {
"Chlorophyll": """叶绿素Chlorophyll是浮游植物进行光合作用的关键色素直接反映水体中藻类的生物量与初级生产力水平。它是评价水体富营养化程度最常用的指标之一。当叶绿素浓度持续升高时表明藻类大量增殖水华风险显著增加并可能引发溶解氧剧烈波动、水体透明度下降及底栖生态系统退化。因此通过遥感手段反演叶绿素浓度可为水华预警、水质改善及生态修复提供重要科学依据。""",
@ -464,11 +464,11 @@ class WaterQualityReportGenerator:
"NH3-N", "BGA", "TT"]
self.parameter_images = {
param: [
f"{param}_histogram.png",
f"{param}_spectrum_comparison.png",
f"{param}_scatter_with_confidence.png",
@ -480,13 +480,13 @@ class WaterQualityReportGenerator:
] for param in params_list
}
# ========== 新增:缓存线程锁 ==========
self._cache_lock = Lock()
cfg = ai_config
def apply_ai_config(self, ai_config: ReportGenerationConfig) -> None: