Skip to content

fix(codex): reject n>1 for google images#1923

Open
steebchen wants to merge 1 commit intomainfrom
codex/reject-google-image-n
Open

fix(codex): reject n>1 for google images#1923
steebchen wants to merge 1 commit intomainfrom
codex/reject-google-image-n

Conversation

@steebchen
Copy link
Copy Markdown
Member

@steebchen steebchen commented Mar 29, 2026

What changed

  • reject image_config.n > 1 for gemini-3-pro-image-preview
  • reject image_config.n > 1 for gemini-3.1-flash-image-preview
  • stop forwarding Google image counts upstream for these chat-completions requests

Why

Google image-preview chat completions were accepting n at the gateway layer, but the local gateway check showed they still returned a single image in practice. The safer behavior is to reject unsupported multi-image requests explicitly instead of implying that n > 1 might work.

Impact

Clients now get a clear 400 with guidance to use n=1 for the two Google image-preview models.

Root cause

The gateway exposed image_config.n, and earlier work attempted to map it into the Google request path. That created the appearance of support even though these models do not provide reliable multi-image output via chat completions.

Validation

  • pnpm vitest run packages/actions/src/prepare-request-body.spec.ts
  • pnpm vitest run apps/gateway/src/api.spec.ts -t "rejects image_config.n > 1"
  • pnpm format
  • pnpm build
  • live local gateway checks against http://localhost:4001/v1/chat/completions for both Google image-preview models with image_config.n: 2

Summary by CodeRabbit

  • New Features

    • Added validation for Gemini image-preview models to enforce single-image requests. Requests with multiple images now return a 400 error with a clear message.
  • Tests

    • Added test cases validating the single-image constraint for supported Gemini models.
  • Refactor

    • Optimized image configuration initialization to only create configuration objects when necessary.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

Walkthrough

This PR adds validation to reject requests with image_config.n > 1 for specific Gemini image-preview models. It introduces a set-based model check in the chat gateway, adds corresponding test coverage, and optimizes image configuration initialization to lazy evaluation.

Changes

Cohort / File(s) Summary
Test Coverage
apps/gateway/src/api.spec.ts
Adds parametrized test suite validating HTTP 400 rejection of requests where image_config.n > 1 for two Gemini image-preview models.
Gateway Validation
apps/gateway/src/chat/chat.ts
Introduces googleSingleImageModels set and enforces that image_config.n must not exceed 1 for these models; throws HTTP 400 if violated.
Request Body Preparation
packages/actions/src/prepare-request-body.ts
Changes generationConfig.imageConfig initialization from unconditional to lazy evaluation using the nullish-coalescing assignment operator.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

auto-merge

Suggested reviewers

  • smakosh
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: rejecting image_config.n>1 for Google image models, which aligns with the primary objective and code modifications across all three files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/reject-google-image-n

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot changed the title [codex] reject n>1 for google images fix(codex): reject n>1 for google images Mar 29, 2026
@steebchen steebchen marked this pull request as ready for review March 29, 2026 13:51
Copilot AI review requested due to automatic review settings March 29, 2026 13:51
Copy link
Copy Markdown

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

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: 7286556cc8

ℹ️ 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 +740 to +742
googleSingleImageModels.has(requestedModel) &&
(image_config?.n ?? 1) > 1
) {
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 n>1 rejection to Google providers only

This check rejects purely by requestedModel, but parseModelInput maps unknown provider/model inputs to requestedProvider === "custom" while preserving the model name. As a result, a custom provider request like acme/gemini-3-pro-image-preview with image_config.n: 2 now fails with a 400 even though the gateway cannot assume Google model limits for custom backends. Please gate this condition on provider/family (non-custom Google paths) so custom OpenAI-compatible providers are not incorrectly blocked.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens gateway validation for Google “image-preview” chat-completions models by explicitly rejecting multi-image requests (image_config.n > 1) that aren’t actually supported, preventing the gateway from implying multi-image output will work.

Changes:

  • Add a gateway-side 400 validation error when image_config.n > 1 is requested for gemini-3-pro-image-preview and gemini-3.1-flash-image-preview.
  • Refactor the model check into a shared Set used for both image input counting and validation.
  • Add API test coverage to ensure image_config.n > 1 is rejected for both models.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
packages/actions/src/prepare-request-body.ts Minor adjustment to Google imageConfig initialization when applying image generation options.
apps/gateway/src/chat/chat.ts Introduces the image_config.n > 1 rejection logic for the two Google image-preview models.
apps/gateway/src/api.spec.ts Adds tests asserting the new 400 behavior for both affected models.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/gateway/src/api.spec.ts (1)

1326-1355: Consider using test.each() for better test isolation and reporting.

The for...of loop pattern works but Vitest's test.each() provides better test isolation, clearer output, and aligns with patterns seen in E2E tests (per learnings about parallel execution with .each()).

♻️ Suggested refactor using test.each()
-	for (const model of [
-		"gemini-3-pro-image-preview",
-		"gemini-3.1-flash-image-preview",
-	]) {
-		test(`/v1/chat/completions rejects image_config.n > 1 for ${model}`, async () => {
+	test.each([
+		"gemini-3-pro-image-preview",
+		"gemini-3.1-flash-image-preview",
+	])("/v1/chat/completions rejects image_config.n > 1 for %s", async (model) => {
 			const res = await app.request("/v1/chat/completions", {
 				method: "POST",
 				headers: {
 					"Content-Type": "application/json",
 					Authorization: "Bearer fake",
 				},
 				body: JSON.stringify({
 					model,
 					messages: [
 						{
 							role: "user",
 							content: "Generate a simple image of a red circle.",
 						},
 					],
 					image_config: {
 						n: 2,
 					},
 				}),
 			});

 			expect(res.status).toBe(400);
 			const json = await res.json();
 			expect(json.message).toContain("image_config.n > 1");
 		});
-	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/src/api.spec.ts` around lines 1326 - 1355, Replace the for...of
loop that creates tests for each model with Vitest's table-driven runner by
converting the looped test blocks into a single test.each(...) call over the
model array; locate the current loop that iterates over
["gemini-3-pro-image-preview", "gemini-3.1-flash-image-preview"] and the inline
test(`/v1/chat/completions rejects image_config.n > 1 for ${model}` ...) and
change it so each model is passed as a parameter to test.each (improves
isolation and reporting) while keeping the same request body shape, assertions
(expect(res.status).toBe(400) and expect(json.message).toContain("image_config.n
> 1")), and usage of app.request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/gateway/src/api.spec.ts`:
- Around line 1326-1355: Replace the for...of loop that creates tests for each
model with Vitest's table-driven runner by converting the looped test blocks
into a single test.each(...) call over the model array; locate the current loop
that iterates over ["gemini-3-pro-image-preview",
"gemini-3.1-flash-image-preview"] and the inline test(`/v1/chat/completions
rejects image_config.n > 1 for ${model}` ...) and change it so each model is
passed as a parameter to test.each (improves isolation and reporting) while
keeping the same request body shape, assertions (expect(res.status).toBe(400)
and expect(json.message).toContain("image_config.n > 1")), and usage of
app.request.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c7a8db19-c87c-410e-929b-655efb1376c4

📥 Commits

Reviewing files that changed from the base of the PR and between a09e6ff and 7286556.

📒 Files selected for processing (3)
  • apps/gateway/src/api.spec.ts
  • apps/gateway/src/chat/chat.ts
  • packages/actions/src/prepare-request-body.ts

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.

2 participants