跳到主要内容
路径文档

附件

源码更新(0.1.5-alpha.1):0.1.3-alpha.1 起 Web 支持上传任意类型的通用文件——文件与图片在同一草稿附件栏混排、后台上传带进度与取消、切换会话后仍可见,模型通过保存的只读路径按需读取,而不接收文件字节。图片路径保留身份、尺寸、媒体类型、规范化提示与只读执行侧路径;编辑前先复制到可写路径,持久附件引用仍按内容寻址。请求字节是按模型路由缓存的投影,不是任意宿主路径。源码:packages/attachment/attachment、packages/attachment/attachment-local、packages/client/ui-attachment 与 packages/fs/tool-fs/src/read-image.ts。

一句话版:ctx.attachments 是持久化二进制附件缝:校验并原子提交不可变的图片字节与通用文件字节,分别返回可序列化的 ImageAttachmentRef 与 FileAttachmentRef。attachment-local 是它的本地文件系统实现,ui-attachment 是纯 React 附件原子。

未发送的浏览器草稿不在这里;字节只在用户提交 prompt 或 provider 适配器提交结构化模型输出时才进入持久存储。

一、是什么​

附件能力解决一件事:把图片与通用文件字节持久化,让会话日志只保存内容寻址引用 + 校验过的元数据,而不是浏览器路径、object URL、provider URL 或 base64。两个核心包都是产品包。

包ctx key角色
attachmentctx.attachments不可变图片/文件引用、图片限制、存储服务
attachment-local(注册在 ctx.attachments 上)DSH_HOME 下的内容寻址私有存储
ui-attachment无(纯呈现层)纯 React 附件原子:混合草稿附件栏、历史图库、大图灯箱

二、引用与写入 API(attachment)​

ctx.attachments 校验并原子提交不可变的图片字节与通用文件字节,分别返回可序列化的 ImageAttachmentRef 与 FileAttachmentRef;消费者从不把浏览器路径、object URL、provider URL 或 base64 持久化进会话事件。

  • validateImage:跑同一套准入策略但不持久化
  • saveImages(rc.7):按序批量提交——先校验全部成员再逐张写;准入失败零写入,存储失败不返回部分引用(已发布的内容寻址对象可能留下,由后续保留策略收集)
  • saveImage:在每个模型可见会话事件发布之前提交每个已接受的图片
  • readImage:按已记录元数据校验内容寻址对象;调用方可取消,实现观察取消并把取消保留为取消(不翻译成存储失败)
  • saveFile / saveFileStream:提交已有字节数组/按背压与取消语义提交有界分块;两种文件写入路径都不设准入限制,字节原样保存
  • readFileStream / fileHostPath:校验式有界分块读取/定位存储对象,供只读按需读取投影
  • admitEncodedFile 与 admitPromptContent:编码文件准入;Host prompt 消费方把有序文本、编码图片和已解析的文件引用一并交给 ctx.attachments.admitPromptContent()
  • isAttachmentError:识别附件错误并按 code 路由,不依赖原型链

未发送的 composer 图片与文件仍是浏览器拥有的临时草稿。

Web 提示词准入(packages/api/session-controller)拒绝既无非空白文本也无附件的消息(0.1.5);仅图片或仅文件的消息仍然有效,空白队列编辑同样被拒绝。

三、本地存储(attachment-local)​

attachment-local 是 attachment 的私有本地实现,默认挂载(base 组合)。

  • 图片对象落在 <DSH_HOME>/attachments/v1/objects/<sha256-prefix>/<sha256>,由不透明的 sha256: id 寻址
  • 通用文件字节的唯一规范对象落在 <DSH_HOME>/attachments/v1/file-objects/<digest-prefix>/<digest>;每条引用路径 <DSH_HOME>/attachments/v1/files/<digest-prefix>/<digest>/<name> 都是只读硬链接,因此名称不同但字节相同的文件不会重复占用磁盘
  • 每个进程把家目录祖先逐级 sync 到文件系统根,证明家目录只 durable 一次,避免误把"别的进程已创建但还没 sync 的目录"当作安全边界
  • 写入用私有暂存目录、owner-only 文件、synced 临时文件、原子排他硬链接发布、发布路径上的目录 sync(POSIX;Windows 依赖文件系统元数据 journaling),保证上报的引用能扛住崩溃
  • 图片的写入准入与读取都完整解码 raster 后才接受格式与尺寸;文件按字节原样保存,不设准入限制,读取时校验完整摘要与记录的字节数
  • DSH_HOME 走共享路径策略:显式配置 → $DSH_HOME → ~/.dsh。会话日志只含引用与校验过的元数据,从不含这个宿主路径
  • readImage 把可选取消转发进文件系统读,并保留取消而不是包成 ATTACHMENT_READ_FAILED
  • 通用文件的字节从不进入 provider 请求:每条路由收到一行确定性 handle 文本,指出文件身份与只读进程路径;执行环境无法映射时,handle 会说明读不到该文件

四、浏览器 UI 原子(ui-attachment)​

纯 React 呈现层:组件保持纯 props,附件数据、上传状态、图片加载与回调都来自声明这些槽位的持有方,字符串经属主插件的 locale 命名空间解析;ui-conversation 与(工具结果图库)ui-tool 是当前消费者。它通过 ctx.slots.inject 等待 conversation.input.attachments、conversation.message.images、conversation.trajectory.images 与 tool.call.images。

  • AttachmentRail:图片与通用文件按选择顺序进入同一条不换行的草稿附件栏,条目统一 64px 高——图片是 64px 方形缩略图,文件是 240px 宽的 DeepSeek Web 卡片(16px 圆角、文档图标、文件名、大写扩展名与字节大小)。溢出隐藏时用边缘箭头翻页,滚动条保持隐藏;文件上传时显示 spinner/进度(首次上报前为不定态),失败显示重试,移除按钮在悬停或键盘聚焦时出现、触摸设备上保持可见(prefers-reduced-motion: reduce 下瞬时滚动)
  • MessageImage / ImageGallery:Chat 把文件与图片放在同一靠右、可换行的排列中并保持来源顺序;单张且无其他附件时按长边 240px 渲染,多附件时每张图显示为 64px 方块,与 240×64px 文件卡同排。加载失败给显式重试;点击打开 ImageLightbox
  • ImageLightbox:文档级模态预览,Escape / 背景点击 / 关闭控件关闭,卸载时焦点还给打开者

模型可见性:无——它在浏览器渲染纯 React 原子,不进任何模型请求。

五、配置​

attachment-local 的准入限制与规范化策略全部可配(0.1.1,默认值见下表);存储根仍由 DSH_HOME 解析。挂载行即默认组合里的:

配置项默认含义
maxImageBytes20 MiB单张图片字节上限
maxImagesPerMessage20每条消息图片数上限
maxMessageImageBytes200 MiB每条消息图片字节总和上限
maxImagePixels64 M单张像素数上限
maxImageDimension8192单边长上限
normalizedImageMaxDimension2048规范化重编码后的单边长上限
normalizedImageMaxBytes4 MiB规范化重编码后的字节上限
imageCompressionConcurrency2(上限 8)压缩/规范化并发
- id: attachment-local
name: '@deepseek-ai/dsh-attachment-local'

提交前每张图按规范化策略做确定性重编码(超限时缩到规范化上限内),落盘的是规范化编码后的字节;saveImage/saveImages 返回的引用旁附带源事实与规范化事实。这些限额只作用于图片:通用文件写入路径不设类型与大小限制。readImageRequest(0.1.1)是"读 + 按模型路由策略投影请求载荷"的合并 API,带请求级去重。

六、已知限制​

  • 光栅图片限制只作用于图片:图片在部署限额内接受 PNG、JPEG、WebP、GIF;其他文件按字节原样保存、不设类型与大小限制,音频与视频暂无专门处理
  • 保留与垃圾回收延后(恢复/派生会话可能共享不可变对象)
  • 持久化未发送草稿、音频与视频需要独立的生命周期与 provider 契约

七、验证​

# 看 attachment-local 是否装载
dsh web --dump-config | grep -iE "attachment"
# 看附件对象目录(owner-only,按 sha256 前缀分桶)
ls -la ~/.dsh/attachments/v1/objects/
# 通用文件:规范对象与只读引用路径
ls -la ~/.dsh/attachments/v1/file-objects/ ~/.dsh/attachments/v1/files/

下一步​

  • 存储层:另一类非会话持久数据(KV 后端),附件不走这里
  • 会话系统:会话日志只存 sha256: 引用与校验过的元数据
  • 内置工具:图片以 ImageBlock 进入 provider 请求,通用文件只投影 handle 文本