Skip to content

支持会话附件删除与图片输入模型兼容降级 #711

Description

@wynxing

目标问题

  1. 附件无法删除导致资源泄漏:用户上传附件后无法删除,切换会话或取消上传时服务端文件残留,缺乏生命周期管理。
  2. 切换到不支持图片的模型后历史图片导致请求失败:用户上传图片后切换模型(如从 Claude 切到纯文本模型),历史消息中的图片内容会导致上游 API 返回 400 错误。
  3. 新会话创建期间被自动切换:Web 端创建新会话时,fetchSessions 回调会自动切换到第一个会话,导致用户被意外跳转。

设计方案

附件删除

  • 从 RuntimePort 拆出 SessionAssetPort 独立接口(接口隔离原则),包含 SaveSessionAsset、OpenSessionAsset、DeleteSessionAsset 三个方法。
  • Gateway 注册 DELETE /api/session-assets/{sessionID}/{assetID} 端点,支持多工作区路由和认证。
  • CLI Bridge 实现 DeleteSessionAsset:校验身份 → 查找会话 → 通过 sessionAssetDeleter 接口删除文件。
  • Web 端取消上传/移除附件时调用 deleteSessionAsset 释放服务端资源。
  • 修复 SaveSessionAsset 缺少会话存在性校验的问题(不存在的 sessionID 之前静默创建文件,现在返回 ErrRuntimeResourceNotFound)。
  • 修复 OpenSessionAsset 缺少 os.ErrNotExist 映射的问题。

图片输入模型兼容降级

  • Runtime 新增 currentInputParts 字段记录当前用户输入的原始 Parts。
  • 在 session_start 阶段调用 rejectUnsupportedCurrentImageInput:若当前模型不支持图片但用户上传了图片,直接拒绝并返回明确错误。
  • 在 prepareTurnBudgetSnapshot 中调用 projectImagesForModelRequest:对历史消息中的图片做投影降级——不支持图片的模型将历史图片替换为占位文本 [历史图片已省略:当前模型不支持图片输入],不修改持久化消息,仅影响发送给模型的内容。

新会话防自动切换

  • Web 端 useSessionStore 新增 _pendingNewSession 内部状态标记。
  • 用户创建/切换新会话时置 true,阻止 fetchSessions 回调自动切换到第一个会话。
  • 会话绑定成功后置 false。

落地清单

  • Gateway:
    • 从 RuntimePort 拆出 SessionAssetPort 独立接口
    • 新增 DeleteSessionAsset 方法和 DeleteSessionAssetInput 结构体
    • 注册 DELETE /api/session-assets/{sessionID}/{assetID} HTTP 端点
    • 多工作区运行时适配 SessionAssetPort,检测下游是否实现该接口
    • 新增 sessionAssetDeleteMethod 到 HTTP ACL 白名单
    • 新增 ErrRuntimeSessionAssetDeleteNotSupported 错误码
  • CLI Bridge:
    • 实现 DeleteSessionAsset 全流程
    • 修复 SaveSessionAsset 缺少会话存在性校验
    • 修复 OpenSessionAsset 缺少 os.ErrNotExist 映射
  • Runtime:
    • 新增 currentInputParts 字段
    • 实现 projectImagesForModelRequest 图片投影降级
    • 实现 rejectUnsupportedCurrentImageInput 拒绝不支持的图片输入
    • 新增 hasImageCapability / partIsImage 辅助函数
  • Web:
    • 新增 deleteSessionAsset() API 方法
    • 新增 session_asset_delete 方法名常量
    • ChatInput 取消上传时调用 deleteSessionAsset
    • useSessionStore 新增 _pendingNewSession 防止自动切换

验收标准

  • 附件删除全链路可用:Web 取消上传 → Gateway 路由 → CLI Bridge 执行删除
  • 删除后 OpenSessionAsset 返回 not found
  • SaveSessionAsset 对不存在的会话返回 ErrRuntimeResourceNotFound
  • 图片投影不污染持久化消息,仅影响模型请求
  • 不支持图片的模型收到占位文本而非图片数据
  • 当前输入含不支持的图片时拒绝并返回明确错误
  • 新会话创建期间不被 fetchSessions 自动切换
  • go build ./... 通过
  • go test ./internal/gateway/... ./internal/runtime/... ./internal/cli/... 通过

测试场景

  • 正常删除附件后再次 Open 返回 not found
  • 对不存在的 sessionID 执行 Save 返回 not found
  • 模型支持图片时历史图片正常传递
  • 模型不支持图片时历史图片被替换为占位文本
  • 当前输入含图片但模型不支持时拒绝
  • 新会话创建期间 fetchSessions 不触发自动切换

破坏性变更说明

  • RuntimePort 移除了 SaveSessionAsset / OpenSessionAsset 方法,拆入独立的 SessionAssetPort 接口。所有 RuntimePort 实现方需适配:不再需要实现附件方法,改为实现 SessionAssetPort。

关联

风险与回滚

  • 风险:SessionAssetPort 拆分是破坏性接口变更,所有 RuntimePort 实现方需适配
  • 风险:SaveSessionAsset 行为变更(不存在的 sessionID 从静默创建变为返回错误)
  • 回滚:恢复 RuntimePort 中的附件方法,移除 SessionAssetPort,移除图片投影逻辑

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions