DEV Community

Cover image for Before I Call GPT Image 2, I Validate These 7 Things
煜华
煜华

Posted on AI-assisted

Before I Call GPT Image 2, I Validate These 7 Things

I used to debug a bad AI product image by rewriting the prompt. Now I check
the request contract first.

A GPT Image 2 API request can be valid JSON and still be wrong for the selected
model. A generic UI can leak a field from another provider. Two dimension
controls can conflict. A timeout can turn one intended image into two
generations. A technically successful output can still change the product.

This is the seven-check preflight I put in front of the generation call.

Disclosure: I work on XPLA. The workflow below is an engineering pattern,
not a promise about price, access, speed or image quality.

1. Select the contract before rendering the form

I do not let one universal form submit every visible field to every image
model. The application selects a model contract first:

const imageContracts = {
  "gpt-image-2": {
    endpoint: "/v1/images/generations",
    allowed: new Set([
      "model", "prompt", "image", "images",
      "size", "aspectRatio", "quality", "replyType", "n"
    ]),
    forbidden: new Set(["imageSize"])
  }
};
Enter fullscreen mode Exit fullscreen mode

The UI should be generated from that record. A control the selected model does
not support should not appear.

2. Reject unknown fields instead of dropping them

Silently removing an unsupported field creates plausible but uncontrolled
outputs. The user chooses a setting, the backend drops it, a default is used,
and everyone blames the model.

function rejectUnknownFields(body, contract) {
  const errors = [];

  for (const key of Object.keys(body)) {
    if (!contract.allowed.has(key)) {
      errors.push(`Unsupported field for ${body.model}: ${key}`);
    }
  }

  for (const key of contract.forbidden) {
    if (body[key] !== undefined) {
      errors.push(`Forbidden field for ${body.model}: ${key}`);
    }
  }

  return errors;
}
Enter fullscreen mode Exit fullscreen mode

An explicit local error is easier to fix than an attractive image produced
from the wrong request.

3. Make one dimension decision

An image form may expose a ratio, a pixel size, a provider-specific size field
and an orientation inferred from the prompt. I allow one supported decision:

function validateDimensions(body) {
  if (body.imageSize !== undefined) {
    return ["imageSize is not valid for this standard contract"];
  }

  const supplied = [
    body.size !== undefined,
    body.aspectRatio !== undefined
  ].filter(Boolean).length;

  return supplied > 1
    ? ["Choose size or aspectRatio, not both"]
    : [];
}
Enter fullscreen mode Exit fullscreen mode

I store the requested orientation beside the output so QA can verify it.

4. Treat a reference image as a rights record

A reference is more than a URL. I store the product identity and the allowed
transformation:

{
  "source_type": "merchant_upload",
  "rights_state": "confirmed_for_internal_calibration",
  "product_id": "merchant-sku-104",
  "variant": "matte-black-500ml",
  "must_preserve": [
    "single bottle",
    "matte black body",
    "silver cap",
    "existing label geometry"
  ],
  "allowed_changes": [
    "background",
    "surface",
    "lighting direction"
  ]
}
Enter fullscreen mode Exit fullscreen mode

A marketplace image being public does not automatically grant permission to
download, transform or use it in advertising.

5. Separate retries from new generations

This is the operational rule I care about most.

If a client times out, it may not know whether the server failed before or
after generation. Repeating the request can create another billable task.

Before submission, I persist:

{
  "generation_id": "img-job-20260902-001",
  "intent": "new_calibration",
  "request_hash": "sha256-of-normalized-request",
  "state": "submitted",
  "attempt": 1,
  "result_state": "unknown",
  "next_action": "check_before-repeating"
}
Enter fullscreen mode Exit fullscreen mode

My state transition is:

draft
  -> approved
  -> submitted
  -> completed
  -> accepted | repair | rejected

submitted
  -> transport_unknown
  -> reconcile before another generation
Enter fullscreen mode Exit fullscreen mode

A new creative candidate receives a new generation ID. An uncertain transport
result enters reconciliation. I do not hide both actions behind one “Try
again” button.

6. Generate one calibration image

For the first run I use:

  • one approved source;
  • one product truth sheet;
  • one background change;
  • one orientation;
  • one output;
  • one named reviewer.

The question is narrow: can the workflow preserve the product facts that
matter? More outputs do not improve the evidence if no one reviews them.

7. Review product truth, not only aesthetics

My QA table looks like this:

Check Expected Decision
Product count one unit accept / repair / reject
Body color matte black accept / repair / reject
Cap silver accept / repair / reject
Label geometry unchanged accept / repair / reject
Invented claims none accept / repair / reject
Orientation requested ratio accept / repair / reject
Crop safety fully visible accept / repair / reject

“Looks good” is not a release decision. An attractive output can still invent
a bundle, accessory, certification or quantity.

Complete preflight

function preflightGPTImage2(body, rightsRecord) {
  const contract = imageContracts[body.model];
  const errors = [];

  if (!contract) errors.push(`Unknown model contract: ${body.model}`);
  if (!body.prompt?.trim()) errors.push("prompt is required");
  if (body.n !== undefined && body.n !== 1) {
    errors.push("Use n: 1 and create separate intentional requests");
  }
  if (body.replyType !== undefined && body.replyType !== "json") {
    errors.push('replyType must be "json"');
  }

  if (contract) {
    errors.push(...rejectUnknownFields(body, contract));
    errors.push(...validateDimensions(body));
  }

  if ((body.image || body.images) &&
      rightsRecord?.rights_state !== "confirmed_for_internal_calibration") {
    errors.push("Reference-image rights are not confirmed");
  }

  return { ok: errors.length === 0, errors };
}
Enter fullscreen mode Exit fullscreen mode

This is only the request boundary. A production implementation still needs
media-type, file-size, URL, privacy, account, policy and storage checks.

Failure classes need different actions

Failure class Example Default action
Client empty prompt fix locally
Contract unsupported field reject before submission
Access unauthorized key stop and fix access
Transport lost response reconcile before repeating
Provider transient failure bounded retry policy
Safety risky request stop or revise
Output QA altered product repair or reject

I keep the final order simple:

contract -> preflight -> approval -> one calibration
-> product QA -> intentional scale
Enter fullscreen mode Exit fullscreen mode

The current XPLA-specific request shape and model-name boundaries are
documented in the GPT Image 2 API guide.
Recheck the live contract before production integration.

Top comments (0)