测试修改
This commit is contained in:
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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:
|
||||
|
||||
Reference in New Issue
Block a user