Skip to content

fix: persist user model mapping intent across prune/bootstrap cycles (#294)#298

Open
panzeyu2013 wants to merge 2 commits into
qxcnm:mainfrom
panzeyu2013:issue-disconnect
Open

fix: persist user model mapping intent across prune/bootstrap cycles (#294)#298
panzeyu2013 wants to merge 2 commits into
qxcnm:mainfrom
panzeyu2013:issue-disconnect

Conversation

@panzeyu2013
Copy link
Copy Markdown
Contributor

Closes #294

问题

auto_associate_source_models() 不感知用户意图——将"从未关联过"和"用户主动解除关联"同等视为"缺失映射"。导致两个 BUG:

场景 链条 结果
Unlink 后立即重建 手动 Unlink → DELETE 映射 → onSuccess 触发 reloadManagedRouting → bootstrap → auto_associate 发现映射不存在 → 用新 ID 重建 enabled=true 映射从未消失
限流恢复后 disabled 被重置 账户变为 limited → prune 删全部映射(含已 disabled) → 账户恢复 → auto_associate 无状态可查 → 全部重建 enabled=true 禁用状态丢失

根源:用户意图只存储在 model_source_mappings 行中,而 prune_stale_*auto_associate 机制无条件删除并重建这些行。

方案

引入 model_source_mapping_preferences 墓碑表,独立于映射存储用户意图:

CREATE TABLE model_source_mapping_preferences (
    source_kind     TEXT NOT NULL,
    source_id       TEXT NOT NULL,
    upstream_model  TEXT NOT NULL,
    preference      TEXT NOT NULL CHECK (preference IN ('unlinked', 'disabled')),
    updated_at      INTEGER NOT NULL,
    PRIMARY KEY (source_kind, source_id, upstream_model)
);

写入点(7 处)

# 位置 触发 操作
W1 delete_managed 用户 Unlink RPC 接收完整 keys → 写入 'unlinked' → DELETE 映射
W2 save_managed 用户保存映射 enabled=false → INSERT 'disabled'true → DELETE 偏好
W3 delete_account 账户永久删除 DELETE preferences WHERE source_kind='openai_account'
W4 delete_aggregate_api API 永久删除 事务内 DELETE preferences/mappings/models
W5 upsert_discovered stale API 下架模型 DELETE 对应 preference
W6 delete_model_source_routes_for_source (prune) source 暂时不可用 不级联 — 偏好跨 prune 存活
W7 sync_* (特定 source_id) 用户请求同步已知不可用 source DELETE preferences — 确定性清理

读取点(1 处)

# 位置 操作
R1 auto_associate_source_models 批量预加载偏好 → 'unlinked' skip / 'disabled' enabled=false / 未命中默认 true

关键设计决策

  • RPC 增强modelSourceMappingDelete 新增 sourceKind/sourceId/upstreamModel 参数,前端已持有直接透传,消除数据库反查和竞态
  • 批量预加载auto_associate 用一次 list_model_source_mapping_preferences 构建 HashMap,循环内 O(1) 匹配,避免 N+1
  • delete_aggregate_api 补齐:原来只删 3 张 API 表,现改为事务版,补齐 model_source_mappingsmodel_source_models 的级联删除
  • payload 模式统一:前端 withAddr({ payload: params }) + Tauri payload: serde_json::Value + Web mapParams 解包,与 save 命令完全对标

改动文件

新增 crates/core/migrations/064_model_source_mapping_preferences.sql

修改 Rust 后端(6 个文件):

  • crates/core/src/storage/mod.rs — struct + migration 注册
  • crates/core/src/storage/model_sources.rs — 表创建 + 4 个 CRUD 方法 + stale 清理追加
  • crates/core/src/storage/accounts.rs — delete_account 级联偏好
  • crates/core/src/storage/aggregate_apis.rs — delete_aggregate_api 重写为事务版 + 补齐 3 层级联
  • crates/service/src/apikey/apikey_models.rs — save/delete/auto_associate 逻辑 + W7 清理点
  • crates/service/src/rpc_dispatch/apikey.rs — RPC 参数扩展

修改前端/桌面(5 个文件):

  • apps/src/lib/api/account-client.ts — 传参扩展
  • apps/src/lib/api/transport-web-commands.ts — mapParams
  • apps/src/hooks/useManagedModels.ts — 签名 + mutation 类型
  • apps/src/app/models/page.tsx — 调用点传完整 mapping
  • apps/src-tauri/src/commands/apikey.rs — Tauri 命令改为 payload 模式

测试修改:

  • crates/service/src/tests/lib_tests.rs — 测试参数补齐

…xcnm#294)

Add model_source_mapping_preferences tombstone table to record user
unlink/disable intent, preventing auto_associate_source_models from
recreating deleted mappings and resetting disabled state after source
recovery from rate limiting.

- New migration 064 for preferences table
- 4 CRUD methods in model_sources.rs storage layer
- W1: delete_managed writes 'unlinked' before deleting mapping
- W2: save_managed writes/clears preference based on enabled state
- W3/W4: delete_account/delete_aggregate_api cascade clean preferences
- W5: stale model cleanup in upsert_discovered cascades to preferences
- W6: prune path preserves preferences (survive temporary unavailability)
- W7: specific-source sync path deletes preferences (known inactive)
- R1: auto_associate bulk-loads preferences, skips unlinked, disables
- RPC enhanced to pass sourceKind/sourceId/upstreamModel alongside id
- delete_aggregate_api rewritten with transaction and 3-level cascade
- Frontend/Tauri/web transport payload pattern aligned with save command
…5 conflict

- Main introduced 064_drop_gateway_error_logs
- Renamed model_source_mapping_preferences migration from 064 to 065
- Both migrations registered sequentially in mod.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Unlink 模型来源映射后被 auto-associate 机制立即重建

1 participant