Skip to content

Commit e2b6d6f

Browse files
committed
[D7] Fix AI config delayed model set crash on close
1 parent 6e99b03 commit e2b6d6f

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

issues/2026-02-10_23-01-37-ui-autosave-streaming-v2.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ D3,Bottom status bar separator/overlap cleanup,Reduce stacked separator lines an
55
D4,"Max tokens UI supports >=1,000,000","Ensure AI配置中心的最大tokens输入允许很大的值(>=1,000,000)且不会出现输入首位数字就被限制/截断的问题;保存/加载配置保持一致。添加pytest-qt回归测试。","Max tokens can be set to 1,000,000+ and persists after reopening AI配置中心; regression test passes.",.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_ai_config_max_tokens_range.py,none,DONE,DONE,TODO,src/gui/ai/unified_ai_config_dialog.py | tests/test_ai_config_max_tokens_range.py,none,user_report: max_tokens max=3999 and typing '4' clamps; verify and prevent regression | test:.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_ai_config_max_tokens_range.py PASS | done_at:2026-02-10
66
D5,Verify provider streaming specs + docs/tests,Use official docs (OpenAI/Anthropic/Gemini) to verify streaming endpoints/headers/params and SSE parsing requirements; adjust provider strategies and AIClient stream parsing if needed; add a short doc summary and tests for OpenAI+Claude streaming fixtures.,Provider streaming endpoints/params match official docs as of 2026-02-10; OpenAI/Claude streaming extraction tests pass; doc added.,.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_provider_streaming_openai_claude.py,manual,DONE,DONE,TODO,src/core/ai_client.py | src/core/ai_providers/openai.py | src/core/ai_providers/claude.py | src/core/ai_providers/gemini.py | docs/ai/streaming_providers.md | tests/test_provider_streaming_openai_claude.py,none,requires web research for up-to-date streaming specs; keep tests offline via fixtures | test:.tmp\ane0305-venv-311\Scripts\python.exe -m pytest -q tests/test_provider_streaming_openai_claude.py PASS | doc:docs/ai/streaming_providers.md | done_at:2026-02-10
77
D6,Ghost accept formatting test aligns with Deep overlay,Update ghost accept formatting regression test to match DeepIntegratedGhostText (non-destructive overlay). Stop relying on OptimalGhostText internal positions/formatting; assert document text unchanged before accept and accepted inserted text uses normal formatting.,tests/test_ghost_text_accept_formatting.py passes; verifies ghost preview does not mutate document and accepted text formatting matches normal text in dark theme.,.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_ghost_text_accept_formatting.py,none,DONE,DONE,TODO,tests/test_ghost_text_accept_formatting.py,none,regression_fail: AttributeError _ghost_start_pos after switching default ghost system to Deep overlay | test:.tmp\ane0305-venv-311\Scripts\python.exe -m pytest -q tests/test_ghost_text_accept_formatting.py PASS | done_at:2026-02-10
8-
D7,Fix AI config delayed model set crash on close,Replace QTimer.singleShot delayed model selection with a dialog-owned QTimer (or guard) so it cannot call into deleted widgets; prevents Qt event loop exceptions during fast open/close flows.,Backup service create-backup-set test runs without Qt event loop exceptions; delayed model set does not crash when dialog closes quickly.,.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_backup_service_create_backup_set.py::test_create_backup_set_backs_up_project_and_vectors,none,TODO,TODO,TODO,src/gui/ai/unified_ai_config_dialog.py,none,regression_fail: QTimer.singleShot calling _set_model_delayed after dialog destroyed -> QComboBox deleted RuntimeError
8+
D7,Fix AI config delayed model set crash on close,Replace QTimer.singleShot delayed model selection with a dialog-owned QTimer (or guard) so it cannot call into deleted widgets; prevents Qt event loop exceptions during fast open/close flows.,Backup service create-backup-set test runs without Qt event loop exceptions; delayed model set does not crash when dialog closes quickly.,.tmp\\ane0305-venv-311\\Scripts\\python.exe -m pytest -q tests/test_backup_service_create_backup_set.py::test_create_backup_set_backs_up_project_and_vectors,none,DONE,DONE,TODO,src/gui/ai/unified_ai_config_dialog.py,none,regression_fail: QTimer.singleShot calling _set_model_delayed after dialog destroyed -> QComboBox deleted RuntimeError | test:.tmp\ane0305-venv-311\Scripts\python.exe -m pytest -q tests/test_backup_service_create_backup_set.py::test_create_backup_set_backs_up_project_and_vectors PASS | done_at:2026-02-10

src/gui/ai/unified_ai_config_dialog.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,20 @@ class UnifiedAPIConfigWidget(QFrame):
160160
def __init__(self, parent=None):
161161
super().__init__(parent)
162162
self._test_worker = None
163+
self._pending_model: str | None = None
164+
self._set_model_timer = QTimer(self)
165+
self._set_model_timer.setSingleShot(True)
166+
self._set_model_timer.timeout.connect(self._apply_pending_model)
163167
self._provider_presets = {} # 先初始化为空字典
164168
self._load_provider_presets() # 加载预设配置
165169
self._init_ui() # 然后创建UI
170+
171+
def _apply_pending_model(self) -> None:
172+
model = (self._pending_model or "").strip()
173+
self._pending_model = None
174+
if not model:
175+
return
176+
self._set_model_delayed(model)
166177

167178
def _init_ui(self):
168179
"""初始化UI"""
@@ -559,7 +570,12 @@ def set_config(self, config: Dict[str, Any]):
559570
# 设置模型(先触发provider变化,再设置模型)
560571
model = config.get("model", "")
561572
if model and hasattr(self, '_model_combo'):
562-
QTimer.singleShot(100, lambda: self._set_model_delayed(model))
573+
self._pending_model = str(model)
574+
try:
575+
self._set_model_timer.stop()
576+
self._set_model_timer.start(100)
577+
except Exception:
578+
QTimer.singleShot(100, lambda: self._set_model_delayed(str(model)))
563579

564580
if hasattr(self, '_temperature_slider'):
565581
self._temperature_slider.setValue(int(config.get("temperature", 0.8) * 100))
@@ -572,7 +588,14 @@ def set_config(self, config: Dict[str, Any]):
572588

573589
def _set_model_delayed(self, model: str):
574590
"""延迟设置模型(等待模型列表更新)"""
575-
self._model_combo.setCurrentText(model)
591+
try:
592+
combo = getattr(self, "_model_combo", None)
593+
if not combo:
594+
return
595+
combo.setCurrentText(model)
596+
except RuntimeError:
597+
# The widget might have been destroyed before the delayed callback fires.
598+
return
576599

577600
def _save_current_scheme(self):
578601
"""保存当前配置方案"""

0 commit comments

Comments
 (0)