Skip to content

feat: keep image-heavy sessions within provider request-size limits - #1508

Merged
RealKai42 merged 12 commits into
mainfrom
kaiyi/perth-v2
Jul 9, 2026
Merged

feat: keep image-heavy sessions within provider request-size limits#1508
RealKai42 merged 12 commits into
mainfrom
kaiyi/perth-v2

Conversation

@RealKai42

@RealKai42 RealKai42 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

A session that accumulates many model-read screenshots can push the serialized
request body over the provider's size ceiling (HTTP 413, e.g. "Request exceeds
the maximum size"), and a single provider-unsupported image (HEIC, oversized
WebP) can poison a session: once it lands in the history, every subsequent
request is rejected, /compact carries the same media and fails identically,
and resuming re-sends the same history.

What changed

  • kosong: classify body-size 413s as a dedicated APIRequestTooLargeError
    (matched on known wordings incl. plain "request (body) too large";
    context-overflow detection runs first so Vertex-style 413 prompt-too-long
    keeps routing to compaction).
  • agent-core: lower the default image downscale cap to 2000px and cap
    model-initiated reads (ReadMediaFile default path) at a 256 KB per-image
    budget — both configurable via [image] in config.toml or
    KIMI_IMAGE_MAX_EDGE_PX / KIMI_IMAGE_READ_BYTE_BUDGET env vars. Explicit
    region / full_resolution reads keep the provider-scale limit as the
    full-fidelity readback escape hatch. The compression ladder gains sub-1000px
    JPEG rungs so small budgets always converge.
  • agent-core: compress oversized WebP instead of passing it through: still
    WebP decodes via a bundled wasm codec (decoder wasm committed as base64 —
    the published CLI is a single-file bundle with no runtime node_modules) and
    re-encodes on the lossless-first PNG/JPEG ladder; animated WebP still passes
    through whole to preserve animation; region crops work on WebP.
  • agent-core: refuse HEIC/HEIF reads with a conversion command matched to
    the execution environment (kaos.osEnv.osKind): built-in sips on macOS,
    heif-convert/ImageMagick on Linux, ImageMagick + winget install hint on
    Windows. The model runs the conversion through Bash under the normal
    permission flow and reads the converted file — the unsupported payload never
    reaches the provider. (A wasm HEIC decoder was evaluated and rejected:
    libheif/libde265 are LGPL and HEVC decoding is patent-encumbered.)
  • agent-core: recover from body-size 413s. Compaction retries its
    summarizer request once with media replaced by [image]/[audio]/[video]
    markers, then falls back to the existing oldest-first shrink. The main loop
    mirrors the strict-resend fallback: the step is resent once with a
    media-degraded projection (all but the two most recent media parts replaced
    by markers; ReadMediaFile's <image path> wrapper survives for re-reads),
    later steps of the turn reuse that projection, the underlying history keeps
    its media, and a resumed session self-heals by re-triggering the same
    deterministic fallback. Observable via projection: 'media-degraded' on the
    llm.request record.
  • docs: [image] section and both env vars documented in zh/en pages.

All changes were developed test-first (413 wording matrix, budget/edge
precedence and scoping, ladder convergence, WebP decode/alpha/animation/crop,
HEIC per-platform guidance, compaction strip-and-retry, loop-level
degraded-resend once-only and stickiness, agent-level end-to-end recovery).
Full monorepo typecheck, lint, and test suite pass; the only failures are 6
pre-existing preflight.test.ts cases that fail identically on the base
branch. One consolidated changeset.

@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9509645

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@9509645
npx https://pkg.pr.new/@moonshot-ai/kimi-code@9509645

commit: 9509645

@RealKai42

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4bea536be3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +510 to +511
let toDegrade = Math.max(0, mediaCount - keepRecent);
if (toDegrade === 0) return messages as Message[];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Degrade media even when one recent item is too large

When the 413 is caused by one or two large media parts, this returns the original messages unchanged because mediaCount <= keepRecent. That can happen with default ReadMediaFile reads despite the new read budget, since the compressor still passes through guarded/unsupported inputs (for example a >64 MB image under the 100 MB media limit, or an inline video), so the single retry in executeLoopStep resends the same oversized body and the session remains stuck. The fallback needs a way to drop or shrink recent media when those parts alone exceed the provider request limit.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0228ac3e92

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +178 to +179
/payload too large/,
/content too large/,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle request-too-large wording

If a 413 response is worded as Request too large or Request body too large, it falls through this list as a plain APIStatusError, so executeLoopStep never takes the new media-degraded resend path and the session remains stuck on providers/proxies that use that unambiguous byte-limit wording. This is distinct from the Vertex token-overflow ambiguity called out above, so the body-size matcher should include the request-too-large/request-body-too-large forms as well.

Useful? React with 👍 / 👎.

- Introduced a new WebP decoding module using @jsquash/webp's wasm decoder.
- Implemented functions to decode WebP images and check for animated WebP formats.
- Updated image compression tests to include scenarios for WebP handling, including encoding and decoding.
- Enhanced error handling for API request size limits to accommodate various error messages.
- Updated pnpm lockfile to include new dependencies for WebP encoding and decoding.
@RealKai42

Copy link
Copy Markdown
Collaborator Author

@codex

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

❌ Nix build failed

Hash mismatch in pnpmDeps:

Hash
specified sha256-hUn5Srn3HnEEzU5DLxgjIzFjI0ukM3iSP4QagftEXdE=
got sha256-i3T30dxNQ7DTWOmUX47kmqpIVtDYby0buPo58+Jhhmw=

Please update flake.nix with the got hash.

@RealKai42 RealKai42 changed the title feat: classify request-body-too-large errors and make the image downscale cap configurable feat: keep image-heavy sessions within provider request-size limits Jul 9, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8022765a02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

originalError: errorMessage(error),
degradedError: errorMessage(degradedError),
});
throw degradedError;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fall back to strict repair after degraded resend 400s

When an oversized history also has a projection defect that only buildMessagesStrict repairs (for example duplicate tool-call ids, a leading assistant, or consecutive assistants), providers that enforce body size before message validation return the 413 first, so this branch sends the media-degraded normal projection. If that smaller request then gets a recoverable structure 400/422, this catch throws it immediately instead of trying a strict+media-degraded projection, leaving the session stuck even though the existing strict fallback could repair the same history once size is no longer the first failure.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea208ec980

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

* owner (KimiCore) on load and reload. Processes that never load config
* (TUI paste, ACP adapter) leave this unset and get env/built-in behavior.
*/
let configuredMaxImageEdgePx: number | undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope image limits to the owning core

Because this configured value is module-global (and configuredReadImageByteBudget below follows the same pattern), constructing or reloading a second KimiCore with a different homeDir/config overwrites the image limits used by all existing sessions and prompt-ingestion paths in the process; SDKRpcClient exposes that multi-client pattern with per-client homeDir. In an embedded app or parallel harness, a session expecting its own [image] max_edge_px / read_byte_budget will suddenly compress using another client's settings, changing request size and image detail. Keep these limits scoped to the core/session or pass them into compression calls instead of storing them globally.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9509645fcb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

originalError: errorMessage(error),
strictError: errorMessage(strictError),
});
throw strictError;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Degrade strict resends that hit 413

If a history is both wire-invalid and too large on a provider that reports the structure 400 before applying the body-size check, the normal call enters this strict branch, but a 413 from the strict resend is thrown here without trying any media-degraded strict projection. That leaves sessions with, for example, duplicate tool-call ids plus accumulated ReadMediaFile images stuck even though repairing the structure and then dropping old media would make the request sendable.

Useful? React with 👍 / 👎.

@RealKai42
RealKai42 merged commit 1bf2c9a into main Jul 9, 2026
11 checks passed
@RealKai42
RealKai42 deleted the kaiyi/perth-v2 branch July 9, 2026 10:05
@github-actions github-actions Bot mentioned this pull request Jul 9, 2026
kermanx added a commit that referenced this pull request Jul 10, 2026
Port v1 #1508 into v2: lower the longest-edge downscale cap back to
2000px (v2 was stuck on the 3000px it had ported from an earlier v1
change) and make it overridable, add the 256 KB read-image byte budget
used by ReadMediaFile, and widen the over-budget fallback ladder to
[2000, 1000, 768, 512, 384, 256].

- MAX_IMAGE_EDGE_PX 3000 -> 2000, with KIMI_IMAGE_MAX_EDGE_PX env and a
  config-pushed value resolved via resolveMaxImageEdgePx.
- READ_IMAGE_BYTE_BUDGET=256KB with KIMI_IMAGE_READ_BYTE_BUDGET env and
  resolveReadImageByteBudget; ReadMediaFile's default compress path now
  uses it (region / full_resolution still honor IMAGE_BYTE_BUDGET).
- Test expectations that hard-coded 3000px / 1500px updated to 2000 /
  1000 to match the v1 behavior.

The [image] config.toml section is intentionally not added: the env
vars already cover the override path, and wiring a config section plus
a runtime push would add v2-specific scaffolding beyond v1.
7723qqq pushed a commit to 7723qqq/kimi-code that referenced this pull request Jul 11, 2026
…oonshotAI#1508)

Integrate upstream image request-size-limits with the local Rust native
module architecture. Rust handles compute (pixel compression/crop), TS
handles policy (image size limits via ImageLimits + global config setters).

Key changes:
- Add @jsquash/webp wasm decoder for WebP format support
- Wire global config setters in KimiCore constructor and setRuntimeConfig
- ImageLimits falls back to global config layer when no per-instance config
- Native compress/crop fall through to TS path for unsupported formats (WebP)
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.

1 participant