Skip to content

feat(api): combo/alias resolution + OpenAI-compatible edits for image routes (#3214, #3215) - #3219

Merged
diegosouzapw merged 1 commit into
release/v3.8.11from
fix/3214-image-routes-combo-edits
Jun 5, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.11from
fix/3214-image-routes-combo-edits

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Closes #3214
Closes #3215

Follow-up to #3205/#3208 (which fixed custom-provider base URL + prefix resolution for generations). Implements the two remaining image-route gaps the reporter (@ngocquynh85) documented.

What changed

Shared model resolver (src/lib/images/imageRouteModel.ts) used by both /v1/images/generations and /v1/images/edits, resolving in order:

  1. built-in image id / alias — untouched
  2. custom provider prefix (myImg/gpt-image-2 → internal <nodeId>/<model>)
  3. bare combo/alias name (image → the combo's single image target) — was "Invalid image model" (Image routes should resolve combo aliases and support OpenAI-compatible image edits #3215 part 1)

/v1/images/edits

resolveImageBaseUrl gained an endpoint: "generations" | "edits" param (defaults to generations; rewrites a base URL that already points at the other image endpoint).

Tests (TDD)

tests/unit/image-routes-combo-edits-3214-3215.test.ts (isolated DATA_DIR):

  • resolveImageBaseUrl edits-endpoint append + cross-endpoint rewrite + fallback
  • parseDataUrl / extractImageEditInputFromJson (data-URL decode + JSON edit shapes)
  • combo/alias → single image target resolution; built-in ids untouched

All additive to currently-failing paths (bare-combo, non-chatgpt-web edit, JSON edit) → no change to working generation/chatgpt-web behavior. Existing image suites stay green (route 7, handler 43, baseurl-3205 6). typecheck + any-budget pass.

… routes (#3214, #3215)

Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".

/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
  unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
  fields the multipart reader produces (was "Invalid multipart body").

The chatgpt-web conversation-continuation edit flow is unchanged.
@diegosouzapw
diegosouzapw merged commit ec4f8c4 into release/v3.8.11 Jun 5, 2026
2 checks passed
@diegosouzapw
diegosouzapw deleted the fix/3214-image-routes-combo-edits branch June 5, 2026 12:37

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces shared model resolution for image routes and adds support for OpenAI-compatible image edits via multipart or JSON payloads, along with corresponding unit tests. Feedback points out a TypeScript compilation error in resolveImageModelPrefix due to a missing id property on the typed node parameter, and suggests improving the robustness of extractImageEditInputFromJson to handle nested image_url objects in standard OpenAI vision formats.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

const nodes = await getProviderNodes({ type: "openai-compatible" });
// node.id (internal UUID) is already a valid internal id; only rewrite when a
// user-defined prefix differs from the node id.
const matched = nodes.find((node: { prefix?: unknown }) => node.prefix === prefixPart);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The parameter node is explicitly typed as { prefix?: unknown }. Consequently, TypeScript infers the type of matched as { prefix?: unknown } | undefined. Accessing matched.id on line 42 will trigger a compilation error because id is not a property of { prefix?: unknown }. Typing node with id?: unknown resolves this type-checking issue.

Suggested change
const matched = nodes.find((node: { prefix?: unknown }) => node.prefix === prefixPart);
const matched = nodes.find((node: { prefix?: unknown; id?: unknown }) => node.prefix === prefixPart);

Comment on lines +142 to +145
else if (entry && typeof entry === "object") {
const e = entry as Record<string, unknown>;
candidates.push(e.image_url ?? e.url ?? e.b64_json);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If a client sends the standard OpenAI vision format where image_url is an object containing a nested url property (e.g., "image_url": { "url": "data:..." }), e.image_url will resolve to an object. Pushing this object directly to candidates will cause parseDataUrl to fail since it expects a string. Handling nested url or b64_json properties makes the parser significantly more robust.

      else if (entry && typeof entry === "object") {
        const e = entry as Record<string, unknown>;
        const imgUrl = e.image_url ?? e.url ?? e.b64_json;
        if (typeof imgUrl === "string") {
          candidates.push(imgUrl);
        } else if (imgUrl && typeof imgUrl === "object") {
          const nested = imgUrl as Record<string, unknown>;
          candidates.push(nested.url ?? nested.b64_json);
        }
      }

if (typeof entry === "string") candidates.push(entry);
else if (entry && typeof entry === "object") {
const e = entry as Record<string, unknown>;
candidates.push(e.image_url ?? e.url ?? e.b64_json);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: b64_json field is accepted but silently dropped — parseDataUrl requires a data: prefix, so raw base64 values (e.g. "iVBORw0...") return null and the image is never extracted. The request then falls through to the route-level "Missing required field: image" error, which is confusing for clients that sent b64_json expecting it to work.

If b64_json support is intended, wrap it with a data-URL prefix before passing to parseDataUrl, or document that only data: URIs are accepted.

HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
… routes (diegosouzapw#3214, diegosouzapw#3215) (diegosouzapw#3219)

Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".

/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
  unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
  fields the multipart reader produces (was "Invalid multipart body").

The chatgpt-web conversation-continuation edit flow is unchanged.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
… routes (diegosouzapw#3214, diegosouzapw#3215) (diegosouzapw#3219)

Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".

/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
  unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
  fields the multipart reader produces (was "Invalid multipart body").

The chatgpt-web conversation-continuation edit flow is unchanged.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
… routes (diegosouzapw#3214, diegosouzapw#3215) (diegosouzapw#3219)

Image routes now resolve a requested model the same way across /v1/images/generations
and /v1/images/edits, via a shared resolver: built-in id -> custom provider prefix ->
bare combo/alias name (e.g. "image" -> its single image target). Previously a bare
combo name fell through to "Invalid image model".

/v1/images/edits gains two capabilities for custom OpenAI-compatible providers:
- multipart edit forwarding to the node's {base_url}/images/edits (was hard-rejected
  unless chatgpt-web);
- JSON/data-URL edit input (images:[{image_url:"data:..."}]), converted to the same
  fields the multipart reader produces (was "Invalid multipart body").

The chatgpt-web conversation-continuation edit flow is unchanged.
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