refactor(desktop): unify API config IPC + drop separate fast model slot#113
refactor(desktop): unify API config IPC + drop separate fast model slot#113
Conversation
…l slot Two cleanups shipped together because both reshape the API config surface: 1. Canonical IPC `config:v1:set-provider-and-models` atomically writes provider + key + model + activate flag and returns full OnboardingState. Existing onboarding:save-key, settings:v1:add-provider, and settings:v1:set-active-provider become thin delegates so back-compat is preserved. Settings → AddProviderModal now syncs Zustand after add — fixes the top-bar pill staleness bug. 2. Drop modelFast field. It was used in exactly one place (applyComment fallback) and indistinguishable from modelPrimary for users, which is why the UI confused everyone with two dropdowns. Schema bumps to v2; v1 configs are accepted and the dead field is dropped on next write. applyComment now falls back to modelPrimary. UI: AddProviderModal / ChooseModel / ModelsTab drop the "fast" dropdown. 337 desktop tests pass. Typecheck + biome clean. Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
-
[Major] Adding a provider now always flips the active provider unexpectedly — this changes runtime model/provider selection and can route subsequent generations to the wrong provider. Evidence:
apps/desktop/src/renderer/src/components/Settings.tsx:334(setAsActive: trueis unconditional).
Suggested fix:const onboarding = await window.codesign.onboarding.getState(); await window.codesign.config.setProviderAndModels({ provider: form.provider, apiKey: form.apiKey.trim(), modelPrimary: form.modelPrimary, ...(trimmedUrl.length > 0 ? { baseUrl: trimmedUrl } : {}), setAsActive: !onboarding.hasKey, });
-
[Major] Silent fallback in Zustand sync path violates the "no silent fallbacks" constraint — failures are swallowed with no UI signal or contextual throw. Evidence:
apps/desktop/src/renderer/src/components/Settings.tsx:764.
Suggested fix:} catch (err) { pushToast({ variant: 'error', title: t('settings.common.unknownError'), description: err instanceof Error ? err.message : String(err), }); }
Summary
- Review mode: initial
- 2 issues found in modified lines: (1) active-provider behavior regression from unconditional activation on add; (2) silent error swallowing in config sync path, conflicting with project error-handling constraints.
- Not found in repo/docs:
docs/VISION.md,docs/PRINCIPLES.mdin this checkout.
Testing
- Not run (automation)
open-codesign Bot
| modelPrimary: form.modelPrimary, | ||
| modelFast: form.modelFast, | ||
| ...(trimmedUrl.length > 0 ? { baseUrl: trimmedUrl } : {}), | ||
| setAsActive: true, |
There was a problem hiding this comment.
Unconditional setAsActive: true makes every newly-added provider become active, which changes generation routing unexpectedly. Consider deriving this from onboarding state (e.g., only true when no configured key exists).
| try { | ||
| const state = await window.codesign.onboarding.getState(); | ||
| setConfig(state); | ||
| } catch { |
There was a problem hiding this comment.
This empty catch silently swallows sync failures. The repo constraint requires surfacing every error in UI or throwing with context; please show a toast or rethrow contextualized error.
…ings Codex flagged the unconditional setAsActive: true — that flips the active provider every time a user adds a backup, silently rerouting subsequent generations. Mirror the legacy add-provider semantics: only flip when no provider is configured yet (first-time setup). Existing active stays put. Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- [Major] Silent fallback swallows sync failure after provider save — this violates the hard constraint that errors must surface in UI or throw with context; if
onboarding:get-statefails, the code still emits a success toast and hides the failure, leaving state potentially stale. Evidenceapps/desktop/src/renderer/src/components/Settings.tsx:765
Suggested fix:try { const state = await window.codesign.onboarding.getState(); setConfig(state); } catch (err) { pushToast({ variant: 'error', title: t('settings.providers.toast.modelSaveFailed'), description: err instanceof Error ? err.message : t('settings.common.unknownError'), }); return; }
Summary
- Review mode: follow-up after new commits
- 1 issue found in changed lines.
Testing
- Not run (automation)
open-codesign Bot
| try { | ||
| const state = await window.codesign.onboarding.getState(); | ||
| setConfig(state); | ||
| } catch { |
There was a problem hiding this comment.
[Major] This catch {} silently ignores failure when reloading onboarding state after save. That conflicts with the project rule that errors must surface in UI or throw with context. Consider surfacing a toast and aborting the success path so users don't get a false success state.
Codex flagged the silent catch in handleAddSave's getState() sync — if the post-mutation store sync fails, a success toast still fires and the user gets stale UI without any error indicator. Surface the failure as an error toast and abort the success path. Signed-off-by: hqhq1025 <1506751656@qq.com>
There was a problem hiding this comment.
Findings
- No actionable issues found in added/modified lines for this revision.
Summary
- Review mode: follow-up after new commits
- No new issues found in this diff.
- Not found in repo/docs:
docs/VISION.md,docs/PRINCIPLES.mdin this checkout. Residual risk: behavior coverage for the new canonicalconfig:v1:set-provider-and-modelspath is still partial (mainly payload validation in current tests).
Testing
- Not run (automation)
open-codesign Bot
Summary
Two cleanups shipped together because they reshape the same surface area (API config / model selection):
1. Canonical IPC for API config mutations
The 3 surfaces that touch API config (top-bar pill, Settings → API 服务, onboarding) used to call 3 different IPC handlers with 3 different return shapes. The Settings 'add-provider' path returned 'ProviderRow[]' and never updated the Zustand store, so the top-bar pill could lag behind reality until a manual reload.
2. Drop `modelFast` model slot
The `modelFast` field was used in exactly one place (`applyComment` fallback) and was indistinguishable from `modelPrimary` for users — the UI showed two near-identical dropdowns and confused everyone.
PRINCIPLES §5b
Test plan
🤖 Generated with Claude Code