diff --git a/.changeset/image-request-size-limits.md b/.changeset/image-request-size-limits.md new file mode 100644 index 000000000..076b0ef78 --- /dev/null +++ b/.changeset/image-request-size-limits.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Keep image-heavy sessions within provider request-size limits: model-read images now honor a 256 KB per-image budget and a 2000px downscale cap (configurable via `[image]` in config.toml or `KIMI_IMAGE_*` env vars), oversized WebP is compressed as well, HEIC/HEIF reads are refused with a platform-matched conversion command instead of poisoning the session, and a request-too-large rejection (HTTP 413) now recovers automatically — the request and /compact both retry with older media replaced by text markers instead of failing the session. + diff --git a/apps/kimi-code/test/tui/controllers/editor-keyboard-image-paste.test.ts b/apps/kimi-code/test/tui/controllers/editor-keyboard-image-paste.test.ts index 0fe14bc09..73e05b7ef 100644 --- a/apps/kimi-code/test/tui/controllers/editor-keyboard-image-paste.test.ts +++ b/apps/kimi-code/test/tui/controllers/editor-keyboard-image-paste.test.ts @@ -141,14 +141,14 @@ describe('clipboard image paste compression', () => { if (att?.kind !== 'image') throw new Error('expected image attachment'); // Stored metadata reflects the compressed size. - expect(Math.max(att.width, att.height)).toBeLessThanOrEqual(3000); - expect(att.placeholder).toContain('3000×1500'); + expect(Math.max(att.width, att.height)).toBeLessThanOrEqual(2000); + expect(att.placeholder).toContain('2000×1000'); // The stored bytes decode to the compressed dimensions — the thumbnail and // the submitted image both read from these bytes, so they cannot diverge. const dims = parseImageMeta(att.bytes); expect(dims).not.toBeNull(); - expect(Math.max(dims!.width, dims!.height)).toBeLessThanOrEqual(3000); + expect(Math.max(dims!.width, dims!.height)).toBeLessThanOrEqual(2000); }); it('records and persists the pre-compression original for an oversized paste', async () => { diff --git a/docs/en/configuration/config-files.md b/docs/en/configuration/config-files.md index f7e893555..303886e36 100644 --- a/docs/en/configuration/config-files.md +++ b/docs/en/configuration/config-files.md @@ -87,11 +87,12 @@ Fields in the config file fall into two categories: **top-level scalars** that d | `thinking` | `table` | — | Default parameters for Thinking mode → [`thinking`](#thinking) | | `loop_control` | `table` | — | Agent loop control parameters → [`loop_control`](#loop_control) | | `background` | `table` | — | Background task runtime parameters → [`background`](#background) | +| `image` | `table` | — | Image compression parameters → [`image`](#image) | | `services` | `table` | — | Built-in external service configuration → [`services`](#services) | | `permission` | `table` | — | Initial permission rules → [`permission`](#permission) | | `hooks` | `array` | — | Lifecycle hooks; see [Hooks](../customization/hooks.md) | -The following sections cover each of the nested tables in turn: `providers`, `models`, `thinking`, `loop_control`, `background`, `services`, and `permission`. +The following sections cover each of the nested tables in turn: `providers`, `models`, `thinking`, `loop_control`, `background`, `image`, `services`, and `permission`. ## `providers` @@ -202,6 +203,17 @@ You can also switch models temporarily without touching the config file — by s In print mode (`kimi -p ""`), Kimi Code runs a single non-interactive turn and exits as soon as the main agent finishes. If you launch background tasks (for example, concurrent subagents via `Agent(run_in_background=true)`) and need them to run to completion, set `keep_alive_on_exit = true`: the process then waits for every background task to reach a terminal state before exiting, bounded by `print_wait_ceiling_s`. Without it, the single turn ending tears background tasks down with the process. +## `image` + +`image` controls how images are compressed before being sent to the model, across every ingestion point (pasted images, `ReadMediaFile` reads, images in MCP tool results, and so on). + +| Field | Type | Default | Description | +| --- | --- | --- | --- | +| `max_edge_px` | `integer` | `2000` | Longest-edge ceiling in pixels. Larger images are scaled down proportionally to fit; raising it preserves more detail at the cost of larger request bodies | +| `read_byte_budget` | `integer` | `262144` (256 KB) | Per-image byte budget for images the model reads for itself (`ReadMediaFile` default reads). It bounds the accumulated request-body size when the model keeps screenshotting and reading images; fine detail stays reachable through the `region` parameter, which reads a crop back at full fidelity (`region` and `full_resolution` are not subject to this budget) | + +`max_edge_px` can be overridden by the `KIMI_IMAGE_MAX_EDGE_PX` environment variable and `read_byte_budget` by `KIMI_IMAGE_READ_BYTE_BUDGET`; both take higher priority than `config.toml`. +