Implement extensions/vision/src/images.ts (read, validate, data URLs) #255
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills#255
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Create
extensions/vision/src/images.ts: resolve local image paths against the working directory, read each file, validate it against DeepSeek's documented limits (format, size, dimensions, count, total body size), and return aValidatedImage[]carrying the metadata and the base64data:URL the API will receive. Every failure throws aToolErrorwith categoryimage.Background
Depends on: #253
This is the local-input boundary of the
visionextension: nothing leaves the machine until every path in the call has passed. The limits are DeepSeek's published numbers, which is why they live in a single injectableValidationLimitsobject — tests exercise the failure paths with tiny synthetic limits instead of allocating 32 MiB buffers.The module is also where a path becomes bytes:
prompt, image order, and the later request body all depend on the returned array preserving the caller's path order.image-size(v2, pure JS, ESM) supplies dimensions and the detectedtype; the format is detected from the file's content, so it is trusted over the file extension.Documentation Required
A separate process downloads these into the listed folders before this issue is implemented. Check the folder for the actual reference material before starting.
docs/reference/image-size/imageSizeFromFile(path)(async) /imageSize(buffer), the returned{ width, height, type, orientation?, images? }shape, anddisableTypes.typevalues per format and the file-read concurrency limit (100). Note: the GitHub repo is archived and read-only; the npm package is still published, which is what this extension depends on.docs/reference/deepseek-api/docs/reference/nodejs/fs.promises.statfor regular-file/size checks.fs.promises.readFilereturning aBuffer.docs/reference/mdn/data:<mediatype>;base64,<data>URL construction.Implementation Details
Add the dependency first (this step is the one that needs it):
npm install image-size@^2and confirmpackage.jsondependenciesgains"image-size": "^2".Public surface:
Rules (each failure is a
ToolErrorwithcategory === "image", message naming the offending path/limit/fix):imageimageimageimageimageimageimages.length >= 15imageimageimageImplementation notes:
path.resolve(cwd, p)so relative paths work fromctx.cwd; preserve the caller's order in the result.imageSizeFromFile(orimageSize(buffer)on the already-read buffer — either is acceptable, but read the buffer anyway because the data URL needs it; preferimageSize(buffer)to avoid a double read). Maptype→ MIME:png→image/png,jpg/jpeg→image/jpeg,gif→image/gif,webp→image/webp; any othertypeis rejected as unsupported.typeback, orimage-sizethrows) must be surfaced as aToolError("image")naming the file and stating it is not a supported image — never leak the rawimage-sizeexception.maxDimension, and against the strictermaxDimensionManywhenpaths.length >= limits.manyImagesThreshold.dataUrl.lengthfor every image plus the prompt/overhead allowance; the signature above has no prompt argument, so use the encoded data URLs plus a fixed overhead constant and document the assumption in a comment (the tool layer passes the real prompt separately; keep the check conservative).dataUrlisdata:${mime};base64,${buffer.toString("base64")}.bytesas the on-disk byte length (stat.size/buffer.length), not the base64 length.Test file
extensions/vision/src/images.test.ts(TDD):Buffer.from(base64, "base64")of a known-good minimal file is the cheapest way).cwd-relative and absolute paths, order preservation, and correctwidth/height/bytes/dataUrlprefix.ValidationLimits(e.g.maxImageBytes: 10,maxImages: 2,maxDimension: 4,manyImagesThreshold: 2,maxDimensionMany: 2) so no large buffers are allocated. Each assertsinstanceof ToolErrorand the expectedcategory.image-sizerecognises but the MIME map does not, and document it), overmaxImageBytes, overmaxImages, overmaxDimension, overmaxDimensionManyat the threshold, overmaxBodyBytes.Acceptance Criteria
package.jsongains"image-size": "^2"andnpm installresolves it.loadAndValidateImagesreturns oneValidatedImageper input path, in order, with absolutepath, mappedmime, dimensions, byte size, and adata:<mime>;base64,...URL.image-size's detectedtype, not from the file extension.ToolErrorwithcategory === "image"and a message naming the file and the limit.imageToolError, never a rawimage-sizeexception.node --test extensions/vision/src/images.test.tspasses from the repo root.Test Plan
Expected: all tests pass with no network access; temp fixtures are created under the OS temp dir and cleaned up.
Manual smoke check with a real image:
Expected: a
ToolErrorwithimagecategory (README.md is not an image), not a crash.pi-loop opened and merged a pull request for this issue: #269