From 9422b49cbf1e97aeca9b8f67f8ea73a376208bcc Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 12 May 2026 17:16:13 -0500 Subject: [PATCH] fix(image): disable attachment resizing by default --- packages/opencode/src/config/attachment.ts | 7 ++++- packages/opencode/src/image/image.ts | 5 +++- packages/opencode/test/image/image.test.ts | 26 +++++++++++++++-- packages/sdk/js/src/v2/gen/types.gen.ts | 1 + packages/web/src/content/docs/config.mdx | 33 ++++++++++++++++++++++ 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/config/attachment.ts b/packages/opencode/src/config/attachment.ts index a5fc599738..340ebd58b2 100644 --- a/packages/opencode/src/config/attachment.ts +++ b/packages/opencode/src/config/attachment.ts @@ -4,8 +4,13 @@ import { Schema } from "effect" import { PositiveInt } from "@opencode-ai/core/schema" export const Image = Schema.Struct({ + enforce_limits: Schema.optional(Schema.Boolean).annotate({ + description: + "Enforce image attachment size limits before sending images to the model. When false, images pass through unchanged (default: false)", + }), auto_resize: Schema.optional(Schema.Boolean).annotate({ - description: "Resize images before sending them to the model when they exceed configured limits (default: true)", + description: + "Resize images before sending them to the model when they exceed configured limits. Requires enforce_limits to be true (default: false)", }), max_width: Schema.optional(PositiveInt).annotate({ description: "Maximum image width before resizing or rejecting the attachment (default: 2000)", diff --git a/packages/opencode/src/image/image.ts b/packages/opencode/src/image/image.ts index 2115e19198..da50663788 100644 --- a/packages/opencode/src/image/image.ts +++ b/packages/opencode/src/image/image.ts @@ -6,7 +6,8 @@ import { Context, Effect, Layer, Schema } from "effect" const MAX_BASE64_BYTES = 4.5 * 1024 * 1024 const MAX_WIDTH = 2000 const MAX_HEIGHT = 2000 -const AUTO_RESIZE = true +const ENFORCE_LIMITS = false +const AUTO_RESIZE = false const JPEG_QUALITIES = [80, 85, 70, 55, 40] const log = Log.create({ service: "image" }) @@ -76,11 +77,13 @@ export const layer = Layer.effect( const normalize = Effect.fn("Image.normalize")(function* (input: MessageV2.FilePart) { const image = (yield* config.get()).attachment?.image const info = { + enforceLimits: image?.enforce_limits ?? ENFORCE_LIMITS, autoResize: image?.auto_resize ?? AUTO_RESIZE, maxWidth: image?.max_width ?? MAX_WIDTH, maxHeight: image?.max_height ?? MAX_HEIGHT, maxBase64Bytes: image?.max_base64_bytes ?? MAX_BASE64_BYTES, } + if (!info.enforceLimits) return input if (!input.url.startsWith("data:") || !input.url.includes(";base64,")) return yield* new InvalidDataUrlError({ url: input.url }) diff --git a/packages/opencode/test/image/image.test.ts b/packages/opencode/test/image/image.test.ts index bf5c0b3948..666f211c3e 100644 --- a/packages/opencode/test/image/image.test.ts +++ b/packages/opencode/test/image/image.test.ts @@ -6,11 +6,24 @@ import { TestConfig } from "../fixture/config" import { testEffect } from "../lib/effect" const it = testEffect(Layer.mergeAll(Image.layer.pipe(Layer.provide(TestConfig.layer())))) +const enforcing = testEffect( + Layer.mergeAll( + Image.layer.pipe( + Layer.provide( + TestConfig.layer({ + get: () => Effect.succeed({ attachment: { image: { enforce_limits: true, auto_resize: true } } }), + }), + ), + ), + ), +) const tiny = testEffect( Layer.mergeAll( Image.layer.pipe( Layer.provide( - TestConfig.layer({ get: () => Effect.succeed({ attachment: { image: { max_base64_bytes: 1 } } }) }), + TestConfig.layer({ + get: () => Effect.succeed({ attachment: { image: { enforce_limits: true, max_base64_bytes: 1 } } }), + }), ), ), ), @@ -28,7 +41,16 @@ function part(mime: string, data: string) { } describe("Image", () => { - it.effect("normalizes generated png and jpeg attachments", () => + it.effect("passes images through by default without loading the resizer", () => + Effect.gen(function* () { + const image = yield* Image.Service + const input = part("image/png", "not-valid-image-data") + + expect(yield* image.normalize(input)).toEqual(input) + }), + ) + + enforcing.effect("normalizes generated png and jpeg attachments when limit enforcement is enabled", () => Effect.gen(function* () { const photon = yield* Effect.promise(() => import("@silvia-odwyer/photon-node")) const source = new photon.PhotonImage( diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index f062700b7d..dda71dee8b 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1143,6 +1143,7 @@ export type McpRemoteConfig = { export type LayoutConfig = "auto" | "stretch" export type ImageAttachmentConfig = { + enforce_limits?: boolean auto_resize?: boolean max_width?: number max_height?: number diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx index ec96069c70..67b7d7ee61 100644 --- a/packages/web/src/content/docs/config.mdx +++ b/packages/web/src/content/docs/config.mdx @@ -345,6 +345,39 @@ You can manage the tools an LLM can use through the `tools` option. --- +### Attachments + +You can configure how image attachments are processed before they are sent to the model through the `attachment.image` option. + +By default OpenCode does not enforce image attachment limits and does not resize images. Images pass through unchanged unless you explicitly enable limit enforcement. + +```json title="opencode.json" +{ + "$schema": "https://opencode.ai/config.json", + "attachment": { + "image": { + "enforce_limits": true, + "auto_resize": false, + "max_width": 2000, + "max_height": 2000, + "max_base64_bytes": 4718592 + } + } +} +``` + +Available options: + +- `enforce_limits` - Enforce configured image limits before sending images to the model. Defaults to `false`, which passes images through unchanged. +- `auto_resize` - Resize images that exceed configured limits. This only applies when `enforce_limits` is `true`. Defaults to `false`. +- `max_width` - Maximum image width when limit enforcement is enabled. Defaults to `2000`. +- `max_height` - Maximum image height when limit enforcement is enabled. Defaults to `2000`. +- `max_base64_bytes` - Maximum base64 payload size when limit enforcement is enabled. Defaults to `4718592`. + +When `enforce_limits` is `true` and `auto_resize` is `false`, images over the configured limits are omitted. When both are `true`, OpenCode attempts to resize oversized images before omitting them. + +--- + ### Models You can configure the providers and models you want to use in your OpenCode config through the `provider`, `model` and `small_model` options.