Repository navigation
fix(ai): keep image sizes inside proportional limits - #59535
cyzroot-jpg wants to merge 2 commits into
Conversation
Snapping each edge to the provider's size multiple can push the shorter edge below a requested aspect that sits at maxRatio, so 1.5K at 1:3 produced 880x2656 (3.018:1) for gpt-image. Repair the rounded size against every limit before returning it. Degenerate shapes such as 0:0 or 0K also produced NaN dimensions, and exact treated them as a success because comparisons with NaN are false. Reject malformed aspects and fall back to the default ratio and tier instead of propagating NaN.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
There was a problem hiding this comment.
The fix works. I sized every aspect from 1:1 to 40:40 at 0.5K–4K against the gpt-image-2 limits. On base, 1,040 of those sizes pass the 3:1 ratio (for example 1:3 at 1.5K gives 880x2656). With this PR none break any limit. The 0:0 and 0K inputs now give 1024x1024 instead of NaN. The packages/ai tests pass. tsc --noEmit passes; the typecheck script itself fails on base too because a check script it calls doesn't exist.
Public API: unchanged.
One suggestion, inline: Media.AspectRatio accepts 0:0 and 1:0, but only the exact() path now rejects them. The OpenAI and Meta paths call fit() directly, so they quietly send 1024x1024 instead. Rejecting zero sides in the schema would cover every provider at once.
| return Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0 | ||
| } | ||
|
|
||
| // A malformed shape degrades to the default rather than producing NaN; `exact` rejects it before sending. |
There was a problem hiding this comment.
This fallback means a request with aspect: "0:0" or "1:0" quietly turns into 1024x1024 for OpenAI (openai-images.ts calls fit(), not exact()) and for Meta. Only the exact() providers report an error. The schema allows these values today because Media.AspectRatio in packages/ai/src/media.ts uses /^\d+(?:\.\d+)?:\d+(?:\.\d+)?$/. Rejecting zero sides there would fail every provider the same way, including xAI, Google and Runway, which pass aspect through unchanged. You could then drop the validAspect checks here.
There was a problem hiding this comment.
Addressed in 1a4f250. Media.AspectRatio now requires both sides to be positive, so OpenAI, Meta and the pass-through providers all reject 0:0 / 1:0 at the request boundary. I dropped the duplicate aspect checks from the sizing helper; tier() still treats 0K as unknown so exact() fails cleanly.
Validate positive sides in Media.AspectRatio so every provider rejects 0:0 and 1:0 at the request boundary instead of only the routes that call ImageSizing.exact(). Drop the duplicate aspect checks from the sizing helper.
There was a problem hiding this comment.
The new commit addresses my earlier comment: Media.AspectRatio now rejects zero-sided aspects for every provider, and the validAspect fallback is gone. I re-ran the #59536 reproduction. On base, 1:3 at 1.5K gives 880x2656, 0K gives NaN, and the schema accepts 0:0 and 1:0. With this PR it gives 896x2656, 0K gives 1024x1024, and the schema rejects both. The packages/ai tests and tsc --noEmit pass.
Public API: Media.AspectRatio (used by ImageRequest.aspect and VideoRequest.aspect) now rejects aspects with a zero side, such as 0:0 or 1:0. Only requests that previously produced NaN sizes are affected.
I have no further concerns.
Issue for this PR
Closes #59536
Type of change
What does this PR do?
fit()clamps the requested aspect tomaxRatiobefore snapping each edge to the provider'smultiple, but the independent snap can land just outside the ratio it just clamped to. The gpt-image path then sends a size such as 880x2656 (3.018:1) for 1:3 at 1.5K. This repairs the rounded size against every limit before returning it, so the result stays inside the ratio as well as the edge and pixel bounds.tier()also accepted0K, and a malformed aspect such as0:0producedNaN. Becausemissed()compares with>/<,NaNslipped through as a success.Media.AspectRationow requires both sides to be positive, so every provider rejects0:0and1:0at the request boundary; non-positive tiers are treated as unknown, andexact()fails instead of returningNaN x NaN.How did you verify your code works?
bun test test/image-sizing.test.tsbun testinpackages/ai(1947 pass, 28 skip)bun run typecheckinpackages/aibunx oxlint packages/ai/src/protocols/utils/image-sizing.ts packages/ai/test/image-sizing.test.tsVisual
Screenshots / recordings
N/A, no UI change.
Checklist
Added by the opencode bot (AI-generated).
Public API: Media.AspectRatio now rejects aspects with a zero side (only affects requests that used to produce NaN sizes)