Skip to content

feat(combos): add imageInput auto|disabled capability control - #1165

Draft
eachann1024 wants to merge 3 commits into
lidge-jun:devfrom
eachann1024:feat/combo-image-input
Draft

feat(combos): add imageInput auto|disabled capability control#1165
eachann1024 wants to merge 3 commits into
lidge-jun:devfrom
eachann1024:feat/combo-image-input

Conversation

@eachann1024

@eachann1024 eachann1024 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add per-combo imageInput: "auto" | "disabled" (default auto).
  • "disabled" strips image from published modalities and rejects image-bearing requests with HTTP 400 before dispatch.
  • Expands previous_response_id first so a continuation that only references prior images still fails closed.
  • Management API sparsifies only explicit "disabled".
  • GUI capability switch defaults on when every target supports images.

Split from #1092 per maintainer request (imageInput must not ride with the effort-picker fix).

Change graph

flowchart LR
  UI["Combo capability switch"] --> Store["OcxComboConfig.imageInput"]
  Store --> Catalog["derive strips image when disabled"]
  Store --> Expand["expand previous_response_id"]
  Expand --> Guard["reject image body HTTP 400"]
  Guard --> Dispatch["combo target dispatch"]
Loading

Screenshots

GUI capability switch (from the original #1092 verification session; same control surface):

Combo capabilities including image toggle

Verification

  • bun test tests/codex-catalog.test.ts tests/combo-management-api.test.ts tests/combo-workspace-data.test.ts tests/server-combo-failover-e2e.test.ts — 211 pass
  • bun run typecheck — exit 0

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Related: #1092

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Added automatic image-capability detection for combos based on configured targets.
    • Added an option to disable image input for text-only combos.
    • Combo configuration screens now display target capabilities with localized controls.
    • Image requests are rejected before processing when image input is disabled.
    • Unsupported image capabilities cannot be enabled for incompatible targets.
    • Improved handling of continued combo requests and invalid continuation state.
  • Documentation

    • Added English and Chinese guidance covering configuration, defaults, constraints, and examples.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (3/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 3/4).
  • GitHub CI is not green on the current head 3db3c78; the CI green box has been unticked.
  • The checklist has been reset: re-test against the latest code and tick the boxes again.

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

3/4 boxes ticked.

GitHub CI is not green on the current head 3db3c78; the CI green box has been unticked.
The checklist has been reset: re-test against the latest code and tick the boxes again.
This PR stays in draft until every box above is ticked.

Hygiene

Deterministic PR hygiene checks passed.

@github-actions
github-actions Bot marked this pull request as draft August 7, 2026 00:32
@github-actions github-actions Bot added the enhancement New feature or request label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Combos now support automatic or disabled image input. The server validates image requests before dispatch, and the catalog, persistence layer, GUI, documentation, and tests reflect the new setting.

Changes

Combo image-input support

Layer / File(s) Summary
Configuration, persistence, and catalog behavior
src/types.ts, src/combos/types.ts, src/server/management/combo-routes.ts, src/codex/catalog/aggregation.ts, tests/codex-catalog.test.ts, tests/combo-management-api.test.ts
Combo configuration accepts "auto" or "disabled", omits the default from persistence, and removes image support from published modalities when disabled.
Request validation and dispatch
src/combos/request.ts, src/combos/index.ts, src/server/responses/core.ts, tests/combos.test.ts, tests/server-combo-failover-e2e.test.ts
Nested input_image values are detected. Continuation requests are expanded before validation and dispatch. Disabled image requests return HTTP 400 without contacting a target.
Workspace capability model and controls
gui/src/combo-capabilities.ts, gui/src/combo-workspace-data.ts, gui/src/components/combo-workspace-*.tsx, gui/src/combo-workspace-types.ts, gui/src/pages/Combos.tsx, gui/src/i18n/*.ts, gui/src/styles-combos-workspace.css, tests/combo-workspace-data.test.ts
The workspace parses model modalities, computes target image support, tracks imageInput, exposes controls in add and detail forms, and persists disabled settings.
Documentation and regression coverage
docs-site/src/content/docs/guides/combos.md, docs-site/src/content/docs/reference/configuration/routing.md, docs-site/src/content/docs/zh-cn/guides/combos.md, docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
English and Chinese documentation describes modality intersection, disabled image input, request rejection, defaults, and configuration constraints.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ComboResponseHandler
  participant ImageInputDetector
  participant ComboTarget

  Client->>ComboResponseHandler: Submit combo request
  ComboResponseHandler->>ComboResponseHandler: Expand previous response state
  ComboResponseHandler->>ImageInputDetector: Inspect expanded request body
  ImageInputDetector-->>ComboResponseHandler: Return image-input presence
  ComboResponseHandler-->>Client: Return HTTP 400 when image input is disabled
  ComboResponseHandler->>ComboTarget: Dispatch expanded request when allowed
Loading

Possibly related PRs

Suggested reviewers: wibias, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding per-combo imageInput control with auto and disabled modes.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
gui/src/components/combo-workspace-types.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import the canonical ModelOption type instead of redefining it.

gui/src/components/combo-workspace-types.ts exports ModelOption, but gui/src/pages/Combos.tsx declares a separate, identical local type. This PR had to add inputModalities?: string[] by hand in both places (and, per the codebase graph, in several other files too). A future field addition can silently drift out of sync in one of the copies.

  • gui/src/components/combo-workspace-types.ts#L11-17: keep this as the single canonical ModelOption export.
  • gui/src/pages/Combos.tsx#L24-24: replace the local type ModelOption = { ... } declaration with import type { ModelOption } from "../components/combo-workspace-types";.
♻️ Proposed fix
-type ModelOption = { provider: string; id: string; namespaced?: string; reasoningEfforts?: string[]; inputModalities?: string[] };
+import type { ModelOption } from "../components/combo-workspace-types";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/combo-workspace-types.ts` at line 1, Use the canonical
ModelOption type exported by combo-workspace-types.ts as the single source of
truth. In Combos.tsx, remove the local ModelOption declaration and import the
type from ../components/combo-workspace-types, preserving all existing usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/combo-capabilities.ts`:
- Around line 5-12: Update comboImagesSupported so incomplete targets are not
filtered out before evaluation: require every target to have nonblank provider
and model values and a matching model advertising image input, while preserving
false for an empty target list. Add a regression test covering one
vision-capable target alongside one incomplete target and verify the result is
false.

In `@src/server/management/combo-routes.ts`:
- Around line 125-134: Separate management API response objects from
runtime-normalized combo objects in the GET /api/combos flow and the PUT
response path. Ensure both responses omit imageInput when it is "auto" and
include it only when explicitly "disabled", while preserving normalized values
for runtime and persistence. Update the affected test expectation in
combo-management-api.test.ts accordingly.

In `@src/server/responses/core.ts`:
- Around line 994-1007: Update the continuation handling around
expandPreviousResponseInput and previousResponseReplayFailure so an unresolved
previous_response_id is rejected in this handler before image policy validation
or child dispatch; ensure image-bearing stored continuations are expanded and
still fail when imageInput is disabled, and add regressions covering both that
case and an unavailable previous_response_id.

In `@src/types.ts`:
- Around line 844-849: Update the normalizedCombo() fixture in
tests/codex-catalog.test.ts to include the required imageInput property, using
the normalized default value expected by NormalizedComboConfig. Preserve the
fixture’s existing object spread and other fields so it remains type-correct.

In `@tests/combo-workspace-data.test.ts`:
- Around line 437-468: Add regression coverage for the disabled image-input
state: test that parseComboList preserves "disabled", draftEquals treats
"disabled" and "auto" as different, and toPutBody emits imageInput: "disabled"
only for disabled drafts while omitting the default auto value. Anchor the tests
to the existing parseComboList, draftEquals, and toPutBody symbols and retain
the current auto-state expectations.

---

Outside diff comments:
In `@gui/src/components/combo-workspace-types.ts`:
- Line 1: Use the canonical ModelOption type exported by
combo-workspace-types.ts as the single source of truth. In Combos.tsx, remove
the local ModelOption declaration and import the type from
../components/combo-workspace-types, preserving all existing usages.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c1cddfda-f280-47e8-9b07-d59f09001dce

📥 Commits

Reviewing files that changed from the base of the PR and between b39eecf and 4849dde.

📒 Files selected for processing (29)
  • docs-site/src/content/docs/guides/combos.md
  • docs-site/src/content/docs/reference/configuration/routing.md
  • docs-site/src/content/docs/zh-cn/guides/combos.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
  • gui/src/combo-capabilities.ts
  • gui/src/combo-workspace-data.ts
  • gui/src/components/combo-workspace-add-modal.tsx
  • gui/src/components/combo-workspace-controls.tsx
  • gui/src/components/combo-workspace-detail-panel.tsx
  • gui/src/components/combo-workspace-types.ts
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Combos.tsx
  • gui/src/styles-combos-workspace.css
  • src/codex/catalog/aggregation.ts
  • src/combos/index.ts
  • src/combos/request.ts
  • src/combos/types.ts
  • src/server/management/combo-routes.ts
  • src/server/responses/core.ts
  • src/types.ts
  • tests/codex-catalog.test.ts
  • tests/combo-management-api.test.ts
  • tests/combo-workspace-data.test.ts
  • tests/server-combo-failover-e2e.test.ts

Comment thread gui/src/combo-capabilities.ts
Comment thread src/server/management/combo-routes.ts Outdated
Comment thread src/server/responses/core.ts
Comment thread src/types.ts
Comment thread tests/combo-workspace-data.test.ts

@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: 4849dde79f

ℹ️ 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".

const targetRoute = routeModel(config, `${pick.target.provider}/${pick.target.model}`);
const childBody = concreteComboRequestBody(
rawBody,
body,

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 Avoid replaying combo continuations twice

When a combo request includes previous_response_id, this now passes an already-expanded body to the child request while leaving previous_response_id in place; the child handleResponsesInner reads that JSON and calls expandPreviousResponseInput again, so every remembered input item is prepended twice before the adapter parses it. This corrupts combo continuations and inflates context/tokens; strip previous_response_id after the parent expansion or only use the expanded body for the image check before dispatching.

Useful? React with 👍 / 👎.

Comment thread gui/src/combo-capabilities.ts Outdated
if (complete.length === 0) return false;
return complete.every((target) => {
const model = models.find(
(row) => row.provider === target.provider.trim() && row.id === target.model.trim(),

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 Honor forward-provider aliases when checking image support

For a combo target that uses a chatgpt/ChatGPT-forward provider, the target picker can show GPT models from the openai catalog row, but this exact provider match cannot find that row and reports imagesSupported=false. In that dashboard path, even vision-capable GPT targets show the image capability as unavailable, so users cannot turn on imageInput: "disabled" from the UI for those combos; reuse the same provider-key expansion as the picker or pass provider metadata into this check.

AGENTS.md reference: gui/AGENTS.md:L7-L10

Useful? React with 👍 / 👎.

Comment thread src/combos/request.ts Outdated
if (record.type === "input_image") return true;
return Object.values(record).some(visit);
};
return visit(body);

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 Limit the image scan to request input

With imageInput disabled this walks the entire request object, not just the actual input being sent to the model. A valid text-only Responses request that carries metadata or a tool schema/example containing { type: "input_image" } is therefore rejected with the combo image error even though no image input would be dispatched; scan only the expanded input content instead of the whole body.

Useful? React with 👍 / 👎.

@eachann1024
eachann1024 force-pushed the feat/combo-image-input branch from 01694ef to 1beda09 Compare August 7, 2026 01:31
@eachann1024
eachann1024 marked this pull request as ready for review August 7, 2026 01:31
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The PR readiness gate passed. I will review the changes.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@github-actions The readiness gate passed. I will review the pull request.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] The readiness gate is complete. The pull request is ready for review.

You are interacting with an AI system.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs-site/src/content/docs/guides/combos.md`:
- Line 267: Update the imageInput descriptions to use capability-intersection
wording: in docs-site/src/content/docs/guides/combos.md:267, state that "auto"
publishes image support only when every target supports images; apply the
equivalent automatic intersection rule in
docs-site/src/content/docs/reference/configuration/routing.md:66; use the same
meaning in Chinese at docs-site/src/content/docs/zh-cn/guides/combos.md:217 and
docs-site/src/content/docs/zh-cn/reference/configuration/routing.md:60. Do not
imply an "enabled" mode or add a separate validation rule.

In `@src/server/responses/core.ts`:
- Around line 1021-1029: Update the continuation check around
expandPreviousResponseInput and unresolvedPrevious so a non-empty
previous_response_id is considered unresolved only when expansion returned the
original body, not when it returned a successfully expanded continuation.
Preserve rejection for missing or corrupt state, allow stored text-only
continuations when combo.imageInput is disabled, and add a regression verifying
the target receives the expanded continuation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5febc4dc-fec4-4f69-a801-bcfe96737d52

📥 Commits

Reviewing files that changed from the base of the PR and between 20c5bd2 and 1beda09.

📒 Files selected for processing (29)
  • docs-site/src/content/docs/guides/combos.md
  • docs-site/src/content/docs/reference/configuration/routing.md
  • docs-site/src/content/docs/zh-cn/guides/combos.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
  • gui/src/combo-capabilities.ts
  • gui/src/combo-workspace-data.ts
  • gui/src/components/combo-workspace-add-modal.tsx
  • gui/src/components/combo-workspace-controls.tsx
  • gui/src/components/combo-workspace-detail-panel.tsx
  • gui/src/components/combo-workspace-types.ts
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Combos.tsx
  • gui/src/styles-combos-workspace.css
  • src/codex/catalog/aggregation.ts
  • src/combos/index.ts
  • src/combos/request.ts
  • src/combos/types.ts
  • src/server/management/combo-routes.ts
  • src/server/responses/core.ts
  • src/types.ts
  • tests/codex-catalog.test.ts
  • tests/combo-management-api.test.ts
  • tests/combo-workspace-data.test.ts
  • tests/server-combo-failover-e2e.test.ts

Comment thread docs-site/src/content/docs/guides/combos.md Outdated
Comment thread src/server/responses/core.ts Outdated

@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: 1beda09f6e

ℹ️ 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 thread src/server/responses/core.ts Outdated
Comment on lines +1021 to +1023
const unresolvedPrevious = typeof (body as { previous_response_id?: unknown } | null)?.previous_response_id === "string"
&& (body as { previous_response_id: string }).previous_response_id.trim().length > 0;
if (combo.imageInput === "disabled" && unresolvedPrevious) {

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 Allow resolved text-only continuations

When imageInput is disabled and a client sends a previous_response_id that is still present in the local replay store, expandPreviousResponseInput() returns a new expanded body but leaves previous_response_id on it. This predicate still treats that successful expansion as unresolved and returns 400 before comboRequestHasImageInput() can inspect the replayed input, so even text-only continuations through a text-only combo are impossible. Track whether expansion missed, or strip the id after successful expansion, before applying this unresolved-state rejection.

Useful? React with 👍 / 👎.

Comment thread src/types.ts
* Omitted / `"auto"` keeps automatic capability derivation (default: enabled when
* the target intersection includes image).
*/
imageInput?: "auto" | "disabled";

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 Preserve imageInput through CLI combo updates

Once a combo has imageInput: "disabled" from the dashboard/API/config, a later ocx combo set cannot include this new field: the CLI builds a full replacement body in src/cli/combo.ts with targets/strategy/sticky/effort/alias only, and the PUT path normalizes an omitted imageInput back to auto. In that CLI update scenario the text-only guard is silently removed, so add a CLI flag or round-trip the existing value before replacing the combo.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

@Wibias
Wibias marked this pull request as draft August 7, 2026 07:22
@Wibias Wibias added stale No activity on a needs-info issue; will close soon unless updated and removed review-ready labels Aug 8, 2026

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The per-combo capability is useful, and splitting it from #1092 keeps the server behavior reviewable. One runtime blocker remains on the current head, in addition to the branch conflicts with current dev.

comboRequestHasImageInput recursively walks the entire request object and returns true for any nested object whose type equals input_image. That includes unrelated surfaces such as tool JSON schemas, tool arguments, metadata, or extension payloads. An image-disabled combo can therefore reject a text-only request with HTTP 400 merely because a declared tool schema contains an enum/default/example like { "type": "input_image" }. Restrict detection to the actual Responses input message/content shapes after continuation expansion, rather than scanning every object value. Add a negative regression where a text-only request declares a tool schema or metadata containing the same string and confirm no rejection or pre-dispatch false positive.

Please also rebase onto the latest dev, resolve the current conflicts, and rerun the combo catalog/management/failover tests plus exact-head CI. The GUI control, copy, and visual placement still require @lidge-jun's final UI review; this request is limited to the runtime false-positive and integration state.

@Wibias

Wibias commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@eachann1024 please update this PR if you still want this or I will have to close it very soon. Thanks for your contribution!

@eachann1024
eachann1024 marked this pull request as ready for review August 10, 2026 02:14
@github-actions
github-actions Bot marked this pull request as draft August 10, 2026 02:15
Per-combo image/multimodal policy: default auto keeps the target
intersection; disabled strips image from catalog modalities and rejects
image-bearing requests (including previous_response_id expansions) with
HTTP 400 before dispatch. Management API sparsifies only explicit
disabled. GUI exposes a capability switch when every target supports
images.
Fail closed for incomplete targets in comboImagesSupported; omit default
imageInput auto from management GET/PUT responses; reject unresolved
previous_response_id when images are disabled; cover disabled draft
persistence and normalizedCombo fixture.
Restrict image-disabled detection to materialised Responses input after
previous_response_id expansion so tool schemas and metadata cannot false-
positive HTTP 400. Treat unresolved continuations by object identity,
strip previous_response_id after a successful expand to avoid double
replay on child dispatch, and align docs with capability-intersection
wording.
@eachann1024
eachann1024 force-pushed the feat/combo-image-input branch from 1beda09 to 3db3c78 Compare August 10, 2026 02:56
@eachann1024

Copy link
Copy Markdown
Contributor Author

Addressed the runtime blocker and rebased onto current dev.

Runtime

  • comboRequestHasImageInput now scans only materialised Responses input (message content / function_call_output.output), not the full request object — tool schemas and metadata with { "type": "input_image" } no longer false-positive HTTP 400.
  • Unresolved previous_response_id for image-disabled combos is detected by expansion identity (body === rawBody), so successful text-only continuations are allowed.
  • After a successful expand, the parent drops previous_response_id before child dispatch to avoid double-prepending history.

Integration

  • Rebased onto latest upstream/dev (resolved conflicts in catalog aggregation, combo management sparse store, and GUI baseline sync key).
  • Docs tables use capability-intersection wording for imageInput auto/disabled.

Verification

  • bun test tests/combos.test.ts tests/codex-catalog.test.ts tests/combo-management-api.test.ts tests/combo-workspace-data.test.ts tests/server-combo-failover-e2e.test.ts — 310 pass
  • bun run typecheck — exit 0

Head: 3db3c782

@github-actions
github-actions Bot marked this pull request as ready for review August 10, 2026 03:18
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The PR is marked ready for review. I will review the current head.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. The PR readiness gate passed at 3db3c7823e656a9e49cbe3f3812cd6aaadc64d1a. The pull request is ready for review.

You are interacting with an AI system.

@Wibias

Wibias commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

CI still needs to be fixed!

@github-actions
github-actions Bot marked this pull request as draft August 10, 2026 03:20

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/codex-catalog.test.ts`:
- Around line 159-168: Add a focused case to the existing imageInput
disabled-mode test using members whose inputModalities are only ["image"], and
assert deriveComboCatalogModel returns null with imageInput set to "disabled".
Keep the current shared text/image assertions unchanged.

In `@tests/server-combo-failover-e2e.test.ts`:
- Around line 1374-1404: Add a focused regression test near the existing
disabled-image continuation test, storing response state whose materialized
input contains input_image content, then continuing it through a combo
configured with imageInput disabled. Assert the request returns HTTP 400 and the
target server receives zero requests, proving replay expansion occurs before
disabled-image validation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 68c7e9ef-89e8-471b-8b9e-f9f85ef5b0b8

📥 Commits

Reviewing files that changed from the base of the PR and between 14c01e5 and 3db3c78.

📒 Files selected for processing (30)
  • docs-site/src/content/docs/guides/combos.md
  • docs-site/src/content/docs/reference/configuration/routing.md
  • docs-site/src/content/docs/zh-cn/guides/combos.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/routing.md
  • gui/src/combo-capabilities.ts
  • gui/src/combo-workspace-data.ts
  • gui/src/components/combo-workspace-add-modal.tsx
  • gui/src/components/combo-workspace-controls.tsx
  • gui/src/components/combo-workspace-detail-panel.tsx
  • gui/src/components/combo-workspace-types.ts
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/Combos.tsx
  • gui/src/styles-combos-workspace.css
  • src/codex/catalog/aggregation.ts
  • src/combos/index.ts
  • src/combos/request.ts
  • src/combos/types.ts
  • src/server/management/combo-routes.ts
  • src/server/responses/core.ts
  • src/types.ts
  • tests/codex-catalog.test.ts
  • tests/combo-management-api.test.ts
  • tests/combo-workspace-data.test.ts
  • tests/combos.test.ts
  • tests/server-combo-failover-e2e.test.ts

Comment on lines +159 to +168
test("imageInput disabled strips image even when every member supports it", () => {
const visionMembers = [
{ provider: "a", id: "m1", contextWindow: 128_000, maxInputTokens: 100_000, inputModalities: ["text", "image"], reasoningEfforts: ["low"] },
{ provider: "b", id: "m2", contextWindow: 128_000, maxInputTokens: 100_000, inputModalities: ["text", "image"], reasoningEfforts: ["low"] },
];
expect(deriveComboCatalogModel("text-only", normalizedCombo({ imageInput: "disabled" }), visionMembers))
.toEqual(expect.objectContaining({ inputModalities: ["text"] }));
expect(deriveComboCatalogModel("vision", normalizedCombo({ imageInput: "auto" }), visionMembers))
.toEqual(expect.objectContaining({ inputModalities: expect.arrayContaining(["text", "image"]) }));
});

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add an image-only disabled-mode regression.

These assertions cover members that share text and image. They do not cover the new inputModalities.length === 0 branch in src/codex/catalog/aggregation.ts after disabled mode removes an image-only intersection.

Add a case with all members exposing only ["image"]. Assert that deriveComboCatalogModel() returns null when imageInput is "disabled".

As per path instructions, “A behavior change in src/ should come with a focused regression test near the existing tests for that subsystem.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/codex-catalog.test.ts` around lines 159 - 168, Add a focused case to
the existing imageInput disabled-mode test using members whose inputModalities
are only ["image"], and assert deriveComboCatalogModel returns null with
imageInput set to "disabled". Keep the current shared text/image assertions
unchanged.

Source: Path instructions

Comment on lines +1374 to +1404
test("disabled image input expands text-only previous_response_id and dispatches once", async () => {
const { rememberResponseState } = await import("../src/responses/state");
rememberResponseState(
{ model: "combo/free", input: [{ role: "user", content: "earlier text" }] },
{
id: "resp_combo_text_prev",
status: "completed",
output: [{ type: "message", role: "assistant", content: "ack" }],
},
);
const bodies: Array<Record<string, unknown>> = [];
const a = serve(async request => {
bodies.push(await request.json() as Record<string, unknown>);
return chatSuccess("continued", "m1");
});
const config = comboConfig({ a: provider("openai-chat", baseUrl(a), "key-a") }, undefined, {
imageInput: "disabled",
});
const response = await post(config, {
previous_response_id: "resp_combo_text_prev",
input: [{ role: "user", content: "next turn" }],
});
expect(response.status).toBe(200);
expect(bodies).toHaveLength(1);
const child = bodies[0]!;
// Parent already expanded; child must not keep previous_response_id (would double-prepend).
expect(child.previous_response_id).toBeUndefined();
const inputText = JSON.stringify(child.input ?? child.messages ?? child);
expect(inputText).toContain("earlier text");
expect(inputText).toContain("next turn");
});

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a stored-image continuation regression.

Lines 1374-1404 prove that text-only replay dispatches once. They do not prove that replay occurs before the disabled-image check. Direct-image coverage still passes if a future change validates before replay expansion.

Store a response with materialized input_image content. Continue it through an image-disabled combo. Assert HTTP 400 and zero target requests.

As per path instructions, “A behavior change in src/ should come with a focused regression test near the existing tests for that subsystem.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/server-combo-failover-e2e.test.ts` around lines 1374 - 1404, Add a
focused regression test near the existing disabled-image continuation test,
storing response state whose materialized input contains input_image content,
then continuing it through a combo configured with imageInput disabled. Assert
the request returns HTTP 400 and the target server receives zero requests,
proving replay expansion occurs before disabled-image validation.

Source: Path instructions

@Wibias Wibias removed the stale No activity on a needs-info issue; will close soon unless updated label Aug 10, 2026
@Wibias

Wibias commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@eachann1024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants