Skip to content

修复 read 工具读取 PDF/图片失败并接入 OCR/视觉附件#5

Open
CharlieZiChen wants to merge 5 commits into
A3S-Lab:mainfrom
CharlieZiChen:fix/read-pdf-image-vision
Open

修复 read 工具读取 PDF/图片失败并接入 OCR/视觉附件#5
CharlieZiChen wants to merge 5 commits into
A3S-Lab:mainfrom
CharlieZiChen:fix/read-pdf-image-vision

Conversation

@CharlieZiChen

Copy link
Copy Markdown

Summary

This PR fixes the desktop sidecar read flow for PDF and image files, and adds multimodal image support for workspace file mentions.

Problems Fixed

  • Fixed readFile() failing on binary files such as PDF/PNG with UTF-8 decode errors like:
    • stream did not contain valid UTF-8
    • Failed to read file ...pdf
    • Failed to read file ...png
  • Added PDF text extraction so readable PDF documents can be summarized by the agent.
  • Added image-safe read behavior so PNG/JPG/WebP/GIF files no longer fail as text reads.
  • Added optional OCR support for image reads via existing @a3s-lab/ocr settings.
  • Added vision attachment support for @/path/to/image.png workspace file mentions, allowing vision-capable models to analyze non-text visual content.
  • Preserved existing text-file behavior for Markdown/source/text files.

Files Changed

  • apps/sidecar/src/modules/kernel/infrastructure/workspace-storage/local-file.storage.ts

    • Replaced raw UTF-8 reads with extension-aware file handling.
    • Added PDF extraction through pdf-parse.
    • Added image metadata extraction and OCR integration.
    • Added clear fallback messages for binary/non-UTF-8 files.
  • apps/sidecar/src/modules/kernel/application/kernel-message-file-context.service.ts

    • Added workspace file mention parsing for @/absolute/path.
    • Appends readable file context to the model input.
    • Converts mentioned workspace images into multimodal attachments for vision-capable models.
    • Restricts file context to the current session workspace and applies file/count/size limits.
  • apps/sidecar/src/modules/kernel/application/kernel-message-run-intake.service.ts

    • Injects mentioned file context before dispatching to the runtime.
    • Merges original pasted images with image attachments produced from workspace file mentions.
  • apps/sidecar/src/runtime/desktop/desktop-kernel-runtime.module.ts

    • Registers KernelMessageFileContextService.
    • Changes LocalFileStorage registration to Nest-managed injection so OCR settings can be read.

Verification

  • Ran targeted TypeScript checks for the modified storage and file-context logic.

  • Verified Markdown/text files still return original UTF-8 content.

  • Verified PNG reads no longer throw UTF-8 errors and return metadata such as type, size, and dimensions.

  • Verified PDF reads no longer throw UTF-8 errors and extract text from a 20-page PDF.

  • Verified invalid/binary files return a clear non-UTF-8/binary-file message instead of crashing.

  • Verified image file mentions generate multimodal attachments:

    • returns one image/png attachment
    • preserves metadata such as Dimensions: 800x1066
  • Verified OCR integration paths:

    • no OCR backend configured: returns a clear “OCR is not configured” message
    • mocked OCR backend configured: appends OCR text: output
  • Built the sidecar successfully with:

    • pnpm --filter @internshannon/sidecar build
  • Started local sidecar and web dev server for manual testing:

    • Sidecar: 127.0.0.1:29653
    • Web: 127.0.0.1:5000
  • Manually confirmed the improved read/image behavior is effective in the running app.

@Aspdelus

Copy link
Copy Markdown
Collaborator

建议结论

暂不建议合并。这个 PR 的修复方向是对的:PDF/图片不应再被当作 UTF-8 文本直接读取并抛错;但当前实现把 readFile() 的职责扩得太宽,引入了文本读取回归、隐式 OCR 网络调用和未 gated 的视觉附件,风险超过了它修复的问题。

主要问题

  • LocalFileStorage.readFile() 对未知扩展名不再尝试 UTF-8 解码,而是直接返回 binary 提示。普通 UTF-8 文本文件如 .lock.patch.diff.proto.mdx.vue.svelte 都可能被误判,和“保留现有文本文件行为”的目标不一致。
  • 图片读取路径里默认触发 OCR。/workspace/read 当前语义是读取文本文件,不应该在 storage 层隐式发起外部 OCR 请求,否则会带来延迟、成本、隐私和失败模式变化。
  • workspace 图片 mention 会直接追加到 images 并触发 streamWithAttachments,但没有检查当前模型是否支持 vision/attachment。文本模型下可能导致请求失败或不可预期行为。
  • 附件上限过大:最多 5 张、每张 20MB,base64 后请求体和内存压力会明显放大。
  • 文件内容被直接拼接进 user prompt,没有作为不可信文件内容隔离,存在 prompt injection 风险。

建议修复方式

  • readFile() 应保持“文本读取”契约:PDF/图片可做专用分支,其余文件先 strict UTF-8 decode,失败后再返回 binary/non-UTF-8 提示。
  • OCR 不应作为默认 readFile() 行为,建议拆成显式 OCR 能力、参数或独立格式读取服务。
  • mention 图片附件应先检查模型能力;不支持 vision 时只注入文件元数据或 OCR 文本,不传 attachment。
  • 降低并增加总附件大小限制,避免单轮请求被图片 base64 放大。
  • 文件上下文应使用明确边界和“不执行文件内指令”的安全说明,或走更隔离的 context/attachment 通道。

验证要求

合并前建议补充单元测试覆盖:

  • UTF-8 未知扩展文本文件仍可读取。
  • 二进制未知扩展返回清晰提示。
  • PDF 文本提取成功、空文本、解析失败。
  • 图片读取不配置 OCR 时不发外部请求。
  • 配置 OCR 时的成功和失败路径。
  • mention 图片在模型支持/不支持 vision 两种情况下的行为。
  • 文件内容截断和附件大小限制。

当前验证结果

我在临时 PR 副本中跑过 pnpm --filter @internshannon/sidecar build,构建通过。GitHub checks 当前没有上报。构建通过说明没有直接编译问题,但不代表行为风险已解决。

@CharlieZiChen CharlieZiChen force-pushed the fix/read-pdf-image-vision branch from 13049a8 to bfa43f7 Compare June 30, 2026 09:57
@CharlieZiChen

Copy link
Copy Markdown
Author

改进目标

本 PR 修复 workspace read 工具读取 PDF/图片时被当作 UTF-8 文本解析导致失败的问题,同时根据评审意见收敛 readFile() 职责,避免在 storage 层隐式触发 OCR 或视觉模型调用。

实现内容

  • 保持 LocalFileStorage.readFile() 的文本读取契约:
    • PDF 走专用文本提取分支。
    • 图片走专用元数据描述分支,仅返回类型、尺寸、大小等信息。
    • 其他未知扩展文件先 strict UTF-8 decode,失败后返回清晰的 binary/non-UTF-8 提示。
  • 新增显式 OCR 能力:
    • 新增 WorkspaceOcrService
    • 新增 POST /api/v1/workspace/ocr
    • 新增 OCR 设置页,可配置默认后端、启用状态、Base URL、Endpoint、请求格式、输出格式、Headers 和 Options。
  • 聊天集成显式 OCR:
    • 仅当用户明确要求 OCR/文字识别/提取文字时,才调用 WorkspaceOcrService
    • OCR 后端失败时直接提示“配置的 OCR 后端不可用”,不会继续让模型自行安装或调用 Tesseract/pdftoppm 等本地命令。
  • 增加图片附件能力 gating:
    • mention 图片仅在当前模型支持 attachment 或 modalities.input 包含 image/vision 时传递视觉附件。
    • 不支持视觉附件时,仅注入文件元数据和说明,不传图片 attachment。
  • 降低附件大小风险:
    • 最多传 2 张图片。
    • 单张图片最大 5MB。
    • 单轮图片总量最大 8MB。
  • 增加文件上下文安全边界:
    • mention 文件内容用 BEGIN/END UNTRUSTED WORKSPACE FILE 包裹。
    • 明确提示模型将文件内容视为不可信参考数据,不执行其中指令。

主要改动文件

  • apps/sidecar/src/modules/kernel/infrastructure/workspace-storage/local-file.storage.ts
  • apps/sidecar/src/modules/kernel/application/kernel-message-file-context.service.ts
  • apps/sidecar/src/modules/kernel/application/kernel-message-run-intake.service.ts
  • apps/sidecar/src/modules/kernel/application/workspace-ocr.service.ts
  • apps/sidecar/src/modules/kernel/presentation/controllers/workspace.controller.ts
  • apps/sidecar/src/modules/kernel/presentation/dto/workspace.dto.ts
  • apps/sidecar/src/modules/kernel/application/kernel-runtime-config.builder.ts
  • apps/web/src/desktop/pages/settings/components/ocr-section.tsx
  • apps/web/src/desktop/pages/settings/SettingsPage.tsx

验证覆盖

已补充并通过单元测试,覆盖评审要求:

  • UTF-8 未知扩展文本文件仍可读取。
  • 二进制未知扩展返回清晰 binary/non-UTF-8 提示。
  • PDF 文本提取成功。
  • PDF 无可提取文本时返回清晰提示。
  • PDF 解析失败时返回清晰提示,不抛出 UTF-8 解码错误。
  • 图片 read 默认只返回元数据,不触发 OCR。
  • 配置 OCR 后端时的成功路径。
  • OCR 后端失败路径。
  • 未启用 OCR 后端时的失败提示。
  • mention 图片在模型支持/不支持 vision attachment 两种情况下的行为。
  • 文件内容截断。
  • 单张图片超过 5MB 时不传 attachment。
  • 多张图片总量超过 8MB 后续不传 attachment。

已运行验证

pnpm --filter @internshannon/sidecar test -- kernel-runtime-config.builder.spec.ts kernel-message-file-context.service.spec.ts workspace-ocr.service.spec.ts local-file.storage.spec.ts --runInBand
pnpm --filter @internshannon/sidecar build
pnpm --filter @internshannon/web desktop:build

结果均通过。

@CharlieZiChen

Copy link
Copy Markdown
Author

本次更新基于最新 origin/main 重新同步,并进一步收敛了 PR 范围。

本次实现内容

  • 移除 OCR 后端相关实现:

    • 删除 WorkspaceOcrService 及其测试。
    • 删除 /workspace/ocr API。
    • 删除设置页中的 “OCR 服务” 入口和相关 UI。
    • 删除聊天流程中显式 OCR 触发、OCR 后端失败中断等逻辑。
    • 现在图片/PDF 的可读性不再依赖 OCR 后端服务。
  • 改进 LocalFileStorage.readFile()

    • PDF:优先用 PDF 文本提取,成功时返回文本;空文本或解析失败时返回清晰说明,不抛 UTF-8 异常。
    • 图片:返回文件类型、大小、尺寸等元数据,不再按 UTF-8 文本解码。
    • 未知扩展文件:先尝试 strict UTF-8 解码,成功则按文本返回;失败才返回 binary/non-UTF-8 提示,避免 .patch.mdx.vue 等 UTF-8 文本回归。
    • 返回内容有截断上限,避免大文件直接撑爆上下文。

当前如何处理 read 工具报错

这次没有禁用 read。对 sidecar 可控的 /workspace/read / LocalFileStorage.readFile() 路径,已经改为 PDF/图片可安全返回,不再出现 stream did not contain valid UTF-8

需要说明的是,聊天中的 SDK 原生 read 工具来自 @a3s-lab/code native 实现,内部仍可能直接按 UTF-8 解码二进制文件;这个 native 实现不在本 PR 的 TypeScript 侧可直接替换。因此本 PR 的处理方式是:

  • 可控 read 路径正确处理 PDF/图片;
  • mention 文件时提前注入可读文本/元数据;
  • 支持 vision 的模型继续走图片 attachment;
  • runtime extra 中提示模型优先使用已注入的文件上下文和附件,减少重复用 SDK 原生 read 读取同一 PDF/图片导致的 UTF-8 报错。

已验证

  • pnpm --filter @internshannon/sidecar test -- local-file.storage.spec.ts kernel-message-file-context.service.spec.ts kernel-runtime-config.builder.spec.ts kernel-session-runtime-factory.service.spec.ts --runInBand 通过。
  • pnpm --filter @internshannon/sidecar build 通过。
  • 后端已重新部署,/api/v1/health 正常。
  • 手动验证 /api/v1/workspace/read 读取 PNG 返回元数据和尺寸,不再抛 UTF-8 错误。
  • OpenAPI 中已无 /workspace/ocr 路径。

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.

2 participants