Cherry Studio 数据迁移实战:ProviderModelMigrator 如何把 Redux 模型状态搬进 SQLite v2

发布时间:2026/9/12 8:16:32
Cherry Studio 数据迁移实战:ProviderModelMigrator 如何把 Redux 模型状态搬进 SQLite v2 Cherry Studio 数据迁移实战ProviderModelMigrator 如何把 Redux 模型状态搬进 SQLite v2【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio导读Cherry Studio 的 v2 架构将原来存放在 Redux 中的 Provider/Model 状态迁入 SQLite 的user_provider与user_model表ProviderModelMigrator正是完成这一动作的专职 migrator。本文围绕该 migrator 的职责、投影projection算法、字段映射、数据质量处理与三阶段执行流程展开结合仓库源码与测试用例帮助你理解增量delta优先的迁移设计如何在不确定的旧数据与不断演进的 registry 之间安全地搬运用户配置并掌握其可复用的实现模式。一、定位v2 迁移管线中的第 6 号 migratorProviderModelMigrator是 Cherry Studio v2 数据迁移管线中负责Provider/Model 域的专用 migrator。在 migratorRegistry.ts 中它排在BootConfigMigrator、PreferencesMigrator、NoteMigrator、MiniAppMigrator、McpServerMigrator之后、AssistantMigrator之前执行元数据如下见 ProviderModelMigrator.tsidprovider_modelnameProvider Modelorder1.75数值越小越先执行它继承自 BaseMigrator.ts遵循统一的prepare → execute → validate三阶段生命周期阶段职责失败返回prepare读取 Redux/Dexie 源数据过滤脏数据、去重、统计条目provider_model_prepare_failedexecute在一个withWriteTx同步事务内插入 provider/model/pin 行provider_model_execute_failedvalidate核对源/目标行数、抽查 API Key 迁移完整性provider_model_validate_failed三个阶段的错误 ID 常量定义在 ProviderModelMigrator.ts任何一阶段失败都会在返回的error字段中以errorId: message的格式暴露给迁移引擎。1.1 四类数据源迁移的数据来源横跨 Redux 与 Dexie 两个旧存储官方文档给出的来源如下数据来源Providers 与 ModelsReduxstate.llm.providers[]Provider/Model 设置Reduxstate.llm.settings置顶模型Pinned modelsDexiepinned:modelsProvider LogoDexieimage://provider-providerId在源码中prepare()通过ctx.sources.reduxState.getCategoryLlmState(llm)读取providers与settings通过ctx.sources.dexieSettings.get(pinned:models)读取置顶模型、通过ctx.sources.dexieSettings.get(\image://provider-${provider.id}) 读取 LogoProviderModelMigrator.ts。一个需要特别注意的设计受管理的 CherryAI 行不会被从 Redux 复制。prepare()会跳过isManagedCherryProviderId(provider.id)命中的行即cherryai/cherry-cloud因为 v2 seederensureCherryAiDefaultProviderAndModelTx已经负责写入规范化的 CherryAI provider 与默认模型旧有的 CherryAI 置顶模型会被改写为指向该 seeded 模型CHERRYAI_DEFAULT_UNIQUE_MODEL_ID。测试 ProviderModelMigrator.test.ts 验证了旧 CherryAI qwen 置顶 → 重写到 seeded 默认模型的行为。附带约束由于本 migrator 独家负责pinned:models的一次性迁移写入pin表、entityTypemodel该键不能再被 classification.json 标记为普通偏好项否则同一份数据会被写入两次。这个约定写在 ProviderModelMigrator.ts 的文件头注释里。二、核心难点Preset 归属投影Ownership Projectionv2 的设计中preset 支撑的行是增量delta不是快照snapshot。也就是说一行user_provider/user_model只存储用户相对出厂默认值的差异其余字段由运行时从当前 registry 解析。这意味着迁移器必须回答一个棘手问题如何区分v1 出厂默认值与用户手动改过的值答案是不能查当前 v2 registry——因为 registry 本身可能已经变了。文档明确给出了方案mappings/v1-provider-model-baseline.json是钉死的 final-v1 基线提取自 v1 修订版d316ec5345680f1de511fd6df3a7fbdb3edad151v1.9.12只包含 provider/model 投影所需的字段。sourceRevision字段记录了该出处。其中的 provider API 标志表示的是post-migration-127/129 的 Redux 形态三个兼容性标志在apiOptions被清空后仍保留在 system-provider 顶层行上。不要用当前的providers.json、models.json或provider-models.json替代此基线做比较。基线文件 v1-provider-model-baseline.json 以sourceRevisionproviders[providerId]组织每个 provider 包含type、apiHost、anthropicApiHost、isNotSupportDeveloperRole、isNotSupportStreamOptions以及按 model id 索引的models快照。源码中对应的 TypeScript 接口V1ProviderModelBaseline/V1ProviderBaseline/V1ModelBaseline定义在 ProviderModelMigrator.ts。2.1 投影规则详解文档归纳了 6 条投影规则逐条拆解如下对照 ProviderModelMigrator.ts 的projectProviderDeltaRow/projectModelDeltaRow自定义 provider 整体保留presetProviderId null的 provider 是用户自定义的其映射出的 provider 配置全部保留为行自有值。其模型仍可能独立命中全局models.json元数据但由于没有有效 preset providerprovider-model override 不可用。preset provider 解析顺序先按providerId解析再按presetProviderId解析。第二个查找是必须的——像 Azure OpenAI 这类自定义 ID 但挂靠已知 provider 类型的实例例如 UUID 形式的0196f996-34fc-7e3f-96d0-10b7f55fd6c8typeazure-openai需要回退到 preset id 才能找到 registry 中的预设。对应resolveEffectivePresetProvider()ProviderModelMigrator.ts与映射层resolvePresetProviderId()ProviderModelMappings.ts。逐字段比较、逐键比较 API 特性provider 字段与 final-v1 基线相等则置 null/缺席API 特性如streamOptions、developerRole按 key 逐个比较一个特性被改动不会冻结其余与基线相等的兄弟标志不同的值才作为行自有 delta 保留。preset 关联的自定义 provider ID 使用 final-v1 自定义 provider API 特性基线非系统 provider 由 v1 迁移 127/129/132 物化出默认方言{ streamOptions: true, developerRole: false }源码常量V1_CUSTOM_PROVIDER_DIALECT_BASELINEProviderModelMigrator.ts。这样既保留了 v1 迁移后显式的特性选择又丢弃了未动过的自定义默认值。模型元数据匹配顺序模型先使用有效 provider 的 provider-model override再匹配全局models.json元数据全局匹配同样适用于完全自定义的 provider。若有效 provider 的 override 在全局元数据中无对应项则 migrator合成一个与运行时一致的 provider-exclusive presetsynthesizePresetFromOverride。preset 模型字段的稀疏规则与 final-v1 模型相等的字段置 null每个非 null 稀疏列在读取时都是权威的不存在单独的 ownership 标记。此外还有两个边界情形若当前存在 preset但模型不在 final-v1 基线中则普通遗留字段没有可证明的用户出处保持 null但显式的capabilities[].isUserSelected选择会保留。endpointTypes在 registry 无法重新推导旧路由元数据时也会保留——包括内置 NewAPI provider 的动态模型、以及typenew-api的自定义 providerCherryIN 模型在没有旧端点元数据时会显式恢复其前缀路由anthropic/→ Anthropic Messages、google/→ Google Generate Content、否则 OpenAI-compatible 回退对应inferCherryInEndpointTypes()ProviderModelMigrator.ts。v1 编辑器合成的0/0定价值等价于缺席 final-v1 价格会在定价归一化时丢弃matchesModelPricingBaseline只比较真实的非 0/0 价格。2.2 为什么必须钉死基线而不是用当前 registry测试 ProviderModelMigrator.test.ts 给出了一个极佳的例子当前 registry 中 OpenAI 的 chat 端点恰好也使用/v1后缀但归属判断必须对照钉死的 final-v1 快照其记录为https://api.openai.com。因此遗留代理地址https://my-proxy.com/v1被判定为用户所有并写入行而defaultChatEndpoint与基线相等则置 null由运行时读取补齐目录事实。这验证了文档的警告拿今天的 registry 与历史数据比较无法区分历史默认值和用户编辑。文档还给出了更新基线的操作规范使用已发布的 final-v1 源修订版而非 v2 registry 快照只保留V1ModelBaseline/V1ProviderBaseline声明的遗留字段更新sourceRevision并运行覆盖基线相等值、真实 delta、自定义 preset ID、provider-exclusive 模型的 migrator 测试。三、目标表与字段映射3.1user_provider表映射文档给出的映射表如下标注了每列的转换规则v1 源v2 目标转换idproviderId直接映射先过滤非法与重复 IDid/typepresetProviderId已知系统 ID 使用目录 ID受支持的自定义 provider 类型链接到其 presetnamename直接保留用户自有值apiHost、anthropicApiHostendpointConfigs转换为按端点键的 baseURL然后对 preset 行投影 final-v1 默认值typedefaultChatEndpoint通过遗留端点映射表转换然后只存储 final-v1 deltaapiKey、认证设置apiKeys、authConfig归一化密钥与 provider 特定凭据遗留 API option 标志apiFeatures转换受支持的标志然后只存储 final-v1 deltaprovider 设置providerSettings归一化 provider 特定用户设置enabledisEnabled默认 true在映射层 ProviderModelMappings.ts 中有若干值得展开的实现细节端点映射表ENDPOINT_MAPL62-L74把遗留字符串端点/类型键映射为EndpointType枚举例如openai→OPENAI_CHAT_COMPLETIONS、openai-response→OPENAI_RESPONSES、anthropic→ANTHROPIC_MESSAGES、gemini/vertexai→GOOGLE_GENERATE_CONTENT、ollama→OLLAMA_CHAT等无法识别且非aws-bedrock的类型会记警告后丢弃。API Key 归一化buildApiKeys会把逗号分隔的 v1 密钥串拆成多个{ id: uuidv4(), key, isEnabled: true }条目AWS Bedrock 在authType apiKey时从settings.awsBedrock.apiKey取密钥。认证配置buildAuthConfigVertexiam-gcp含 project/location/serviceAccount、AWS Bedrockapi-key-aws/iam-aws、Azure OpenAIiam-azure apiVersion、CherryIN OAuth、以及普通api-key。遗留 Anthropic Web OAuth 被端到端移除——token 原本存放在独立的凭据文件中、已不再读取因此此类 provider 会被重置回api-key认证路径避免 v2 行落入不可恢复状态。provider 设置keepAliveTimeollama/lmstudio/gpustack、rateLimit、extraHeaders来自extra_headers、notes、cacheControl来自anthropicCacheControl会被归一化进providerSettings。3.2user_model表映射v1 源v2 目标转换provider ID modelidid、providerId、modelId构建确定性providerId::modelId身份有效 registry 匹配presetModelId全局 preset与 provider 出处无关或合成的 provider-exclusive preset未匹配的自定义模型为 nullname、description、group同名可空列自定义行完整值preset 行为 final-v1 deltacapabilitiescapabilities归一化能力名保留显式isUserSelected选择endpoint_type、supported_endpoint_typesendpointTypes归一化遗留端点别名supported_text_deltasupportsStreaming自定义行默认 truepreset 行为 final-v1 deltapricingpricing归一化为运行时定价丢弃合成的空0/0回显源顺序orderKey在每个 provider 内分配分数键实现要点模型唯一 ID 通过createUniqueModelId(providerId, modelId)生成非法 ID 返回 null 并在 prepare 阶段被剔除ProviderModelMigrator.ts。capability 映射CAPABILITY_MAP把 v1 类型映射到 v2 能力vision→IMAGE_RECOGNITION、reasoning→REASONING、function_calling→FUNCTION_CALL、embedding→EMBEDDING、rerank→RERANKtext/web_search除外用户显式关闭的能力isUserSelected false优先于重复的启用条目且不会被端点隐含能力重新加回ProviderModelMappings.ts。pricing 映射只迁移 v2 定价契约能无损表达的货币——$→ USD、¥/→ CNY其余货币记警告后丢弃输出{ input: { perMillionTokens, currency }, output: { perMillionTokens, currency } }ProviderModelMappings.ts。orderKeyprovider 顺序键在 seeded CherryAI 行之后生成generateOrderKeySequenceBetween模型顺序键按 provider 作用域分配assignOrderKeysByScope保证 UI 排序稳定。四、有意丢弃或重新推导的数据文档列出了一批不落库的数据这背后是读时解析read-time resolution的设计哲学preset 模型无 delta 的字段不存由当前 registry 在读取时解析不存储userOverrides所有权数组。registry 独有的 provider 端点字段不复制进 preset 行如modelsApiUrls、adapterFamily。但迁移后的自定义 relay 可能保留 main-only 的adapterFamily提示——因为没有目录能重新推导它。测试 ProviderModelMigrator.test.ts 覆盖了小米 MIMO 这种自定义 relay 的回填场景无目录匹配时运行时推断 anthropic 协议族避免 resolver 回退到 openai-compatible 导致 404。preset 的inputModalities、outputModalities、token 限制、reasoning、参数支持不在迁移期从当前 registry 推断null 委托给读时解析。遗留isNotSupportEnableThinking没有 v2ApiFeatures目标直接丢弃。遗留 Anthropic Web OAuth token 在源外不可恢复对应 provider 回到 API-key 认证路径。损坏标识符、重复行保留首次出现、retired providers、无效 pin 引用、缺失的可选 Logo 均按下一节规则处理。五、端点路由边界Endpoint Routing Boundary文档用一小节明确了路由数据的职责划分Renderer/API 端点写入只包含用户可编辑的baseUrl值。遗留adapterFamily路由出处可能存在于主进程存储形态的自定义 provider 中但它不是共享写入 DTO 的一部分。Preset provider 的路由族一律取自当前 registry。也就是说v2 的端点配置契约刻意把用户能改的baseUrl与系统推导的adapterFamily 等路由元数据分离。迁移时对 catalog 匹配的系统 providerprojectProviderDeltaRow会把 endpointConfigs 削减到只剩 baseUrl 级别的 delta而对无目录匹配的自定义 provider才保留LEGACY_TYPE_TO_ADAPTER_FAMILY推导出的路由提示openai-compatible/openai/anthropic/google/newapi/gateway/ollama见 ProviderModelMappings.ts并且 Anthropic Messages 端点刻意跳过该提示——v1 自定义 anthropic relay 即使端点说 anthropic 协议、type也常为openai端点协议必须优先。六、数据质量处理清单prepare()阶段集中完成脏数据过滤文档给出如下处理矩阵问题处理provider ID 缺失/为空跳过并警告provider ID 重复保留第一个并警告model ID 缺失、为空或不安全route-unsafe跳过并警告候选 model ID 全部非法保留 provider、剔除非法模型、汇总警告provider 内 model ID 重复保留第一个并警告Retired provider跳过并警告缺失 final-v1 preset 基线保守保留映射后的 provider 值并警告无效的置顶模型引用丢弃缺失可选 Logo保留 provider 但不带 Logo源码中的实现细节值得注意提前过滤的意义缺失/空providerId若不过滤会以空字符串主键落进user_providerSQLite 文本主键允许从而遮蔽整个 v2 数据层的查找ProviderModelMigrator.ts 的注释。route-unsafe model idcreateUniqueModelId会拒绝含、#等路由不安全字符的 ID测试用例jackrong-qwopus3.5-27b-v3?、legacy-model#fragment均被剔除且不影响同一 provider 的其余模型迁移见 ProviderModelMigrator.test.ts。置顶模型归一化normalizePinnedModelId支持对象形态{id, provider}、JSON 字符串、provider/model、provider::model、以及裸provider::model唯一 ID 五种格式并会与迁移后的有效模型集合做交叉校验去重ProviderModelMigrator.ts。测试验证了旧顺序被保留、非法引用被丢弃ProviderModelMigrator.test.ts。6.1 事务与 Logo 处理的联动provider 行与 model 行在一个同步withWriteTx事务内插入ctx.db.transaction(...)orderKey保持 prepare 阶段准备好的顺序seeded CherryAI 行之后。事务内部还有一处精心安排的依赖顺序ProviderModelMigrator.ts先ensureCherryAiDefaultProviderAndModelTx(tx)写入 seeded CherryAI 行插入 Logo 的file_entry其file_entry_id外键依赖文件实体先存在插入 provider 行、按 BATCH_SIZE100 分批插入 model 行插入 Logo ref 行其source_id外键依赖 provider 行已存在最后写入pin表的置顶模型行onConflictDoNothing。事务结束后migrator 通过assertOwnedForeignKeys对自己拥有的provider_logoref 表执行PRAGMA foreign_key_check——因为迁移全程foreign_keys OFF见 BaseMigrator.ts插入期不会暴露外键错误必须主动自检。若事务失败已写盘的 WebP Logo 文件会被unlinkPreparedImages清理避免重试时产生孤儿文件。6.2 Logo 的两种迁移路径v1 的自定义 provider Logo 存在 Dexie 的image://provider-id键下取值有两种形态源码注释 ProviderModelMigrator.tsdata URLbase64 上传图或小体积内联图→ 提升为磁盘上的 WebPfile_entry由 ref 行引用logoKey置 null并打上delete_when_unreferenced清理策略——与运行时bindLogoImage路径一致provider 被删或换 Logo 时可回收。测试用 1×1 PNG 验证了 WebP 落盘与 ref 行唯一性ProviderModelMigrator.test.ts。非 data: 值v1PROVIDER_LOGO_MAP[id]的 hashed 构建资源路径→ 在 v2 中已失效直接写logoKey会渲染破图。因此从资源名反查品牌并重新表达为 v2 的icon:catalogKey引用recoverV1ProviderLogoIconKey无法识别的值回退为 null内置 provider 按 id 取内置图标、自定义 provider 显示首字母头像。测试覆盖了openai-a1b2c3d4.png → icon:openai、microsoft.png → icon:azureai、字面量poe → icon:poe、未知值丢弃等场景ProviderModelMigrator.test.ts。七、validate 阶段的完整性校验validate()ProviderModelMigrator.ts执行三类核对行数核对user_provider非 CherryAI/非 CherryCloud 行数 prepare 统计的 provider 数user_model同理pin表entityTypemodel行数 归一化后的置顶数。任一不匹配都会产出provider_count_mismatch/model_count_mismatch/pin_count_mismatch错误。API Key 抽查对前 5 个 provider 抽样若源有apiKey而目标apiKeys为空且buildProviderApiKeys确实能产出可迁移条目则报missing_api_key_providerId。统计输出返回sourceCount/targetCount/skippedCount供引擎汇总。八、实现文件速查文件职责ProviderModelMigrator.tsprepare/投影/事务/pin/校验 主逻辑mappings/ProviderModelMappings.ts遗留 → v2 字段转换端点、API Key、认证、能力、定价mappings/v1-provider-model-baseline.json钉死的 final-v1 归属基线含sourceRevisiontests/ProviderModelMigrator.test.ts迁移与归属投影回归测试mappings/tests/ProviderModelMappings.test.ts字段转换单测九、小结可复用的迁移设计模式ProviderModelMigrator提供了一套值得借鉴的迁移范式钉死基线而非对照活 registry用带sourceRevision出处信息的静态基线快照判断历史默认 vs 用户改动避免 registry 演化污染归属判定delta 优先的稀疏落库只存非默认差异其余字段读时解析让行数据随时间跟随 registry 演进prepare 期集中清洗非法/重复/退役数据在事务前剔除并聚合警告保证 execute 阶段幂等、可控事务内依赖排序 外键自检file_entry → owner 行 → ref 行配合PRAGMA foreign_key_check主动兜底关闭外键约束的迁移期源侧一次性所有权pinned:models由本 migrator 独家迁移防止 codegen 重复写入。理解这套迁移逻辑不仅能解释 v1 → v2 升级后为什么我的自定义 provider 配置还在、而默认值悄悄让位给了目录也能为你在 Cherry Studio 中排查升级后的 Provider/Model 异常、或为其他数据域设计迁移器提供直接参考。【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询