• thzxx released this 2026-07-21 13:14:42 +08:00 | 5 commits to master since this release

    版本概述

    v0.3.10 是一个聚焦于配置保存链路健壮性的修复版本。针对 v0.3.9 版本中用户反馈的"切换 Provider 后首次保存提示 LLM 配置不全,第二次才成功"的问题进行了根本性修复,并顺带消除了一批预存的隐患。

    本版本无破坏性变更,所有改动向后兼容。


    一、核心修复:LLM 配置批量保存(config:setBatch IPC)

    1.1 问题描述

    设置页一次保存 8 个 LLM 配置字段(provider、model、apiKey、baseURL、numCtx、3 个 contextWindow)。v0.3.9 之前前端采用串行 config:set 保存:

    • 第 1 步保存 llm.provider 时触发 C-1 修复逻辑:Provider 变化会清空 llm.apiKey
    • 同时触发 reloadAdapter() 重建 Adapter,但此时 llm.apiKey 已被清空且新值尚未写入
    • reloadAdapter() 读取到空 apiKey,返回 false
    • 前端收到 success: false,toast 报错"LLM 配置不完整"
    • 但所有字段最终都已写入 DB,第二次点保存时配置已完整,显示"已保存"

    用户感知:明明全部填好了,第一次点保存报错,第二次才成功。

    1.2 解决方案:config:setBatch IPC

    新增 config:setBatch IPC handler(electron/ipc/handlers.ts:768-920),接收 {key, value}[] 数组,按以下顺序原子化处理:

    1. 参数校验:非空数组、每个元素结构合法、value 类型合法(string|number|boolean|null)
    2. 第一步循环:逐条写入 configService.set(key, value) + 审计日志(脱敏)
    3. 第二步:统一触发一次 reloadAdapter()(仅在 LLM 字段变更时)
    4. 第三步:统一应用 Engine/Orchestrator 配置更新
    5. 第四步:日志级别即时应用
    6. 第五步:workspace.path 写入独立文件

    关键改进:reloadAdapter 只在所有字段都写入后调用一次,彻底消除中间态。

    1.3 前端联动改造

    SettingsModal.handleSave

    src/components/settings/SettingsModal.tsx:435-473 从串行 8 次 config.set 改为一次性 setBatch

    const entries = [
      { key: 'llm.provider', value: provider },
      { key: 'llm.model', value: model },
      { key: 'llm.apiKey', value: apiKey },
      { key: 'llm.baseURL', value: baseURL },
      { key: 'ollama.numCtx', value: numCtx },
      { key: 'deepseek.contextWindow', value: dsCtxWindow },
      { key: 'agnes.contextWindow', value: agnesCtxWindow },
      { key: 'mimo.contextWindow', value: mimoCtxWindow },
    ];
    const r = await setBatch(entries);
    

    OnboardingWizard.handleNext

    src/components/onboarding/OnboardingWizard.tsx:26-60Promise.allSettled 并行 6 次 config.set 改为 setBatch

    额外修复的预存隐患:旧实现 Promise.allSettled 只检查 status === 'rejected',完全忽略 { success: false } 的情况,导致配置实际失败但前端显示成功并关闭向导。新实现正确处理 !r.success 分支。

    1.4 preload + 类型声明


    二、workspace.path 失败感知修复

    2.1 问题描述

    config:setconfig:setBatch 在写入 workspace.path 到独立文件(workspace-config.json)失败时,仅记录 log.error 后继续返回 success: true

    用户感知:看到"配置已保存",但下次启动仍使用旧 workspace 路径,误以为配置系统故障。

    2.2 修复

    handlers.ts:751-763(config:set)和 handlers.ts:912-922(setBatch)的 catch 块改为:

    catch (err) {
      log.error('[CONFIG] Failed to save workspace path:', err);
      return { success: false, error: `工作空间路径保存失败:${(err as Error).message}` };
    }
    

    前端 useConfig hook(SettingsModal.tsx:95-105)已有 !r.success 的回滚 UI + toast 处理逻辑,无需改前端。


    三、Provider 切换防御性顺序修复

    3.1 问题描述

    setBatch 原实现:在循环内处理 llm.provider 时,若检测到 Provider 变化,会清空 llm.apiKey。这依赖一个隐含约定:entries 数组中 llm.provider 必须出现在 llm.apiKey 之前。

    当前前端实现确实如此,但这是脆弱的隐含依赖。若未来开发者误把 llm.apiKey 放在 llm.provider 之前:

    1. 先 set llm.apiKey(用户填写的值)
    2. 处理 llm.provider 时检测到变化,清空 llm.apiKey
    3. 用户填写的 apiKey 丢失

    3.2 修复

    handlers.ts:813-828:将 Provider 切换清空 apiKey 的逻辑从循环内移到循环前

    // 循环前:先扫描 entries 找出 llm.provider 的值
    const providerEntry = entries.find((e) => e.key === 'llm.provider');
    if (providerEntry) {
      const oldProvider = configService.get<string>('llm.provider') ?? '';
      const newProvider = (providerEntry.value as string) ?? '';
      if (oldProvider && newProvider && oldProvider !== newProvider) {
        configService.set('llm.apiKey', '');
      }
    }
    
    // 然后循环按 entries 顺序 set(包括 llm.apiKey),最终值正确
    

    兼容性:前端 entries 顺序正确时,新旧实现行为完全一致;顺序错误时,新实现仍能正确保存 apiKey。


    四、正确性审查结论

    本次修复已经过完整审查,确认:

    • setBatch 错误返回路径下的中间态可接受(DB 是 source of truth,下次发消息会再次尝试 reloadAdapter)
    • Engine/Orchestrator 更新顺序与原 config:set 完全一致
    • useConfig hook 保留 config.set 不与 setBatch 冲突(分别用于单字段实时保存和多字段批量保存)
    • 前端代码无其他遗漏的多字段 config.set 调用场景
    • Provider 切换清空 apiKey 的竞态处理正确

    五、修改文件清单

    文件 改动类型 说明
    electron/ipc/handlers.ts 新增 + 修改 新增 config:setBatch IPC(150 行);config:set 的 workspace.path 错误返回
    electron/preload.ts 新增 暴露 setBatch API
    src/types/global.d.ts 新增 MetonaConfigAPI 接口新增 setBatch 类型
    src/components/settings/SettingsModal.tsx 修改 handleSave 改用 setBatch
    src/components/onboarding/OnboardingWizard.tsx 修改 handleNext 改用 setBatch
    package.json 修改 版本号 0.3.9 → 0.3.10
    package-lock.json 修改 版本号同步
    README.md 修改 版本徽章 0.3.9 → 0.3.10

    六、升级说明

    • 从 v0.3.9 升级:直接拉取 master 分支即可,无 DB schema 变更,无配置文件格式变更
    • 首次安装:参考 README.md 中的快速开始
    • 配置兼容性:v0.3.9 及之前版本保存的配置完全兼容,无需迁移

    七、致谢

    感谢用户反馈"切换 Provider 后首次保存报错"的问题,这次反馈直接推动了 setBatch IPC 的设计实现,让配置保存链路从根本上变得更健壮。

    Downloads