Skip to content

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

@wynxing

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions