Files
AstrBot/dashboard/CHAT_CONFIG_BINDING_FIX.md
Lishiling bcd7e67394 fix: 增加安全访问函数
- 给 localStorage 访问加了 try/catch + 可用性判断:dashboard/src/utils/chatConfigBinding.ts:13
- 新增 getFromLocalStorage/setToLocalStorage(在受限存储/无痕模式下异常时回退/忽略)
- getStoredDashboardUsername() / getStoredSelectedChatConfigId() 改为走安全读取:dashboard/src/utils/chatConfigBinding.ts:36       - 新增 setStoredSelectedChatConfigId(),写入失败静默忽略:dashboard/src/utils/chatConfigBinding.ts:44
- 把 ConfigSelector.vue 里直接 localStorage.getItem/setItem 全部替换为上述安全方法:dashboard/src/components/chat/ConfigSelector.vue:81
- 已重新跑过 pnpm run typecheck,通过。
2026-02-21 21:46:12 +08:00

7.5 KiB
Raw Blame History

Chat 新会话配置绑定修复说明

0. 变更摘要(改了哪些文件)

  • dashboard/src/utils/chatConfigBinding.ts
  • dashboard/src/composables/useSessions.ts
  • dashboard/src/components/chat/ConfigSelector.vue
  • dashboard/src/components/chat/StandaloneChat.vue
  • dashboard/src/components/chat/Chat.vue

1. 问题背景

Dashboard 在创建新会话时会调用接口:

  • GET /api/chat/new_session

但创建完成后并没有把“当前选择的配置文件abconf”绑定到新会话对应的对话路由上导致新会话会继续使用后端的默认配置。

在现有实现中,配置文件与对话的绑定关系依赖配置路由接口:

  • POST /api/config/umo_abconf_route/update

ConfigSelector.vue 已经使用该接口来把配置绑定到当前对话(通过 UMO 作为 key但“新会话创建”流程没有做同样的绑定操作因此会出现新会话配置丢失的问题。

2. 修复思路(做了什么)

核心思路:新会话创建成功后,立刻根据该会话的 UMO 调用 /api/config/umo_abconf_route/update 进行绑定

UMO 的构造与 ConfigSelector.vue 保持一致(platformId:messageType:sessionKey),避免“前端绑定的 UMO 与后端路由表 key 不一致”导致绑定无效。

同时为了不影响正常使用,绑定失败不会阻断会话创建(仅输出错误日志)。

3. 具体改动(逐文件)

3.1 dashboard/src/composables/useSessions.ts

修改点:newSession()

  • 读取用户最近一次选择的配置 id
    • 来源:localStoragechat.selectedConfigId(通过 getStoredSelectedChatConfigId() 读取)
  • 创建新会话后(拿到 sessionIdplatformId),在满足以下条件时自动绑定:
    • selectedConfigId !== 'default'
    • platformId === 'webchat'(当前 UMO 构造逻辑为 webchat 专用)
  • 绑定方式:
    • 使用 buildWebchatUmoDetails(sessionId, false) 生成 umo
    • 调用:
      • POST /api/config/umo_abconf_route/update
      • payload{ umo, conf_id: selectedConfigId }
  • 错误处理:
    • 绑定失败不会 throw避免导致新会话创建失败console.error('Failed to bind config to session', err)
  • 清理调试代码:
    • 移除临时加入的 console.warn(...) 调试日志
    • 移除调试用的二次校验请求(不再请求 GET /api/config/umo_abconf_routes

关键代码(简化后):

const selectedConfigId = getStoredSelectedChatConfigId();
const { session_id: sessionId, platform_id: platformId } = (await axios.get('/api/chat/new_session')).data.data;

currSessionId.value = sessionId;

if (selectedConfigId !== 'default' && platformId === 'webchat') {
  const { umo } = buildWebchatUmoDetails(sessionId, false);
  await axios.post('/api/config/umo_abconf_route/update', { umo, conf_id: selectedConfigId });
}

为什么这样改:

  • useSessions.newSession() 是主聊天页创建新会话的唯一入口(Chat.vue 通过它创建会话),把绑定逻辑放在这里可以一次性修复所有创建新会话的场景。
  • 使用与 ConfigSelector.vue 相同的 UMO 格式,确保后端路由表能正确命中。
  • 限制 platformId === 'webchat' 是为了避免对非 webchat 平台生成错误 UMO 并写入路由表。

3.2 dashboard/src/components/chat/StandaloneChat.vue

修改点:bindConfigToSession() / newSession()

  • bindConfigToSession(sessionId)
    • props.configId 为空或为 default 则跳过
    • 使用 buildWebchatUmoDetails(sessionId, false) 生成 umo
    • 调用 POST /api/config/umo_abconf_route/update 绑定 conf_id: props.configId
  • newSession()
    • GET /api/chat/new_session 成功后,优先调用 bindConfigToSession(sessionId)best-effort
    • 绑定完成后再设置 currSessionId
  • 清理调试代码:
    • 移除临时加入的 console.warn(...) 调试日志
    • 移除调试用的二次校验请求(不再请求 GET /api/config/umo_abconf_routes

关键代码(简化后):

async function bindConfigToSession(sessionId: string) {
  const confId = (props.configId || '').trim();
  if (!confId || confId === 'default') return;
  const { umo } = buildWebchatUmoDetails(sessionId, false);
  await axios.post('/api/config/umo_abconf_route/update', { umo, conf_id: confId });
}

async function newSession() {
  const sessionId = (await axios.get('/api/chat/new_session')).data.data.session_id;
  await bindConfigToSession(sessionId);
  currSessionId.value = sessionId;
}

为什么这样改:

  • StandaloneChat.vue 自己实现了会话创建逻辑(不走 useSessions),因此需要在这个组件内补齐同样的绑定动作。
  • 先绑定后激活 currSessionId可以降低“UI 已开始使用该会话但配置尚未绑定”的窗口期(尤其是首次进入组件时的自动建会话)。

3.3 dashboard/src/components/chat/Chat.vue

修改点:仅清理调试日志(无业务逻辑变更)

  • 移除调试用的 console.warn(...),包括:
    • 组件加载/挂载日志
    • 发送消息前“无会话则创建会话”的提示日志

为什么这样改:

  • 这些日志用于验证修复是否被调用,确认生效后应移除,避免污染浏览器控制台与用户反馈日志。

3.4 dashboard/src/utils/chatConfigBinding.ts

修改点:新增公共工具(集中管理 “选择的配置 id” 与 UMO 构造)

  • 新增常量:
    • CHAT_SELECTED_CONFIG_STORAGE_KEY = 'chat.selectedConfigId'
  • 新增方法:
    • getStoredSelectedChatConfigId():从 localStorage 读取当前选中的配置 id为空则返回 default
    • getStoredDashboardUsername():读取 localStorage.user(为空则返回 guest
    • setStoredSelectedChatConfigId(configId):向 localStorage 写入当前选中的配置 id写入失败时静默忽略
    • buildWebchatUmoDetails(sessionId, isGroup):按 platformId:messageType:sessionKey 的格式生成 webchat 的 UMOConfigSelector.vue 逻辑一致)
  • 增加安全性:
    • localStorage.getItem/setItem 增加 try/catch 与可用性判断,避免在 Safari 无痕/受限存储等环境中抛异常导致页面崩溃

为什么这样改:

  • 之前 ConfigSelector.vueuseSessions.tsStandaloneChat.vue 都需要同一套“storage key / UMO 拼接”规则,分散实现容易出现不一致(导致绑定不生效)。
  • 抽成一个 utils 后,可以保证新会话绑定与配置选择器使用完全一致的 UMO/Key。
  • 同时把 localStorage 访问集中到一个位置做防护,减少各处自行访问带来的稳定性风险。

3.5 dashboard/src/components/chat/ConfigSelector.vue

修改点:复用公共 storage 访问与 username 读取(无业务逻辑变化)

  • 不再直接访问 localStorage,改为复用 chatConfigBinding.ts 中的安全方法:
    • getStoredDashboardUsername()
    • getStoredSelectedChatConfigId()
    • setStoredSelectedChatConfigId()

为什么这样改:

  • 避免多个文件分别访问 localStorage 导致实现不一致或因受限存储环境抛异常。
  • useSessions.ts 读取到的“上次选择配置 id”与 ConfigSelector.vue 持久化/回写的是同一份数据与同一套兼容逻辑。

4. 额外说明

  • 本次修复复用并遵循现有的配置绑定机制(umo_abconf_route 路由表),不改变后端接口语义。
  • 绑定失败不会阻止新会话创建:这样即使后端配置路由接口异常,用户仍可继续使用默认配置进行聊天,避免前端功能不可用。