docs/design/dual-role-image-generation-models.md
Proposed, 2026-08-20.
This document defines a backward-compatible model configuration change that
allows one configured model route to be used both as a normal conversation
model and by the built-in image_gen tool.
Add one flat optional field to model provider entries:
{
"supportsImageGeneration": true
}
Keep the existing imageOnly field, but narrow its meaning to one concern:
whether the route is excluded from normal conversation-model selection.
The effective rules are:
supportsImageGeneration: true makes a route eligible for image_gen.imageOnly: true prevents a route from being used as a primary or other
ordinary content-generation model.imageOnly: true entries remain eligible for image_gen even when
supportsImageGeneration is absent.false when absent, except for the
legacy implication above.This separates an output capability from a selector restriction without renaming or invalidating existing settings.
The current model uses imageOnly for two independent decisions:
That representation works for dedicated generators such as an image-only
model, but it cannot represent a route that supports both normal conversation
and image generation. Marking such a route imageOnly: true makes it eligible
for /model --image, but also removes it from the main model list and causes
primary-model selection to fail.
The two concerns must be independent:
image-generation capability != image-generation exclusivity
The name supportsImage is intentionally not used. Qwen Code already uses
image capability terminology for image input and visual understanding through
capabilities.vision, generationConfig.modalities.image, and
isImageCapable(). Image input and image output are distinct capabilities.
imageModel.imageOnly: true configuration without migration.imageOnly as the selector-only restriction for dedicated generators.capabilities.vision,
modalities.image, a model name, or any other image-input signal.imageModel.image_gen prompt, size, permission, download, storage, or
artifact semantics.imageOnly behavior already
applies.imageOnly.The model can receive and understand images in a normal content-generation
request. Existing signals such as capabilities.vision and
generationConfig.modalities.image describe this direction.
The configured route can satisfy the built-in image_gen transport contract
and produce an image from a prompt. The new supportsImageGeneration field
describes this direction.
The configured route is reserved for the image-generation selector and must
not be used for ordinary content generation. The existing imageOnly field
continues to describe this restriction.
modelProviders entries are configured routes. Two entries may represent the
same physical provider model when they intentionally use different endpoints
or provider identities.
Add the flat field to ModelConfig, AvailableModel, and provider template
types that currently carry imageOnly:
interface ModelConfig {
/** Whether this route can be used by the built-in image_gen tool. */
supportsImageGeneration?: boolean;
/** Whether this route is restricted to the image generation selector. */
imageOnly?: boolean;
}
The same names and meanings must survive provider-template expansion and model registry resolution.
All image-generation eligibility checks use one shared helper:
export function isImageGenerationCapable(model: {
supportsImageGeneration?: boolean;
imageOnly?: boolean;
}): boolean {
return model.supportsImageGeneration === true || model.imageOnly === true;
}
The imageOnly fallback is the compatibility rule. It must not be duplicated as
ad hoc boolean expressions across callers.
supportsImageGeneration | imageOnly | Image selector / image_gen | Primary model |
|---|---|---|---|
absent or false | absent or false | unavailable | allowed |
true | absent or false | available | allowed |
true | true | available | rejected |
absent or false | true | available for compatibility | rejected |
If a configuration explicitly sets supportsImageGeneration: false together
with imageOnly: true, imageOnly wins for backward compatibility. The route
remains image-generation-capable and image-only. No migration or warning is
required.
Dual-role route:
{
"id": "omni-model",
"name": "Omni Model",
"envKey": "MODEL_API_KEY",
"baseUrl": "https://gateway.example.com/model-api",
"supportsImageGeneration": true
}
Dedicated image route using the explicit new form:
{
"id": "image-model",
"name": "Image Model",
"envKey": "MODEL_API_KEY",
"baseUrl": "https://images.example.com/api/v1",
"supportsImageGeneration": true,
"imageOnly": true
}
Legacy dedicated image route, which remains valid:
{
"id": "image-model",
"envKey": "MODEL_API_KEY",
"baseUrl": "https://images.example.com/api/v1",
"imageOnly": true
}
This change separates selection semantics only. It does not add a second endpoint to one provider entry.
The current image-generation service validates the selected route's explicit
HTTPS baseUrl, reads the route's envKey, and calls the existing DashScope
multimodal-generation path. A dual-role entry therefore works only when its
configured baseUrl and credential are valid for both its normal provider
traffic and the existing image-generation transport.
When chat and image generation require different endpoints or credentials,
configuration must continue to use two model routes: one ordinary route and
one imageOnly route. A future endpoint-override design may add flat fields for
that case, but it is not part of this change.
Normal model discovery continues to exclude only imageOnly: true routes.
Setting supportsImageGeneration: true alone must not remove a route from the
main model list, Arena, provider selection, ACP selection, or other ordinary
content-generation paths.
/model --image and the image-model dialog include routes for which
isImageGenerationCapable(model) returns true.
Capability remains necessary but not sufficient. Existing validation still requires:
Primary-model validation continues to reject only imageOnly: true. A route
with supportsImageGeneration: true and no imageOnly restriction remains a
normal primary model.
Selecting a capable route as imageModel continues to hot-register
image_gen. Clearing or replacing imageModel, safe mode, bare mode, and tool
permission behavior remain unchanged.
supportsImageGeneration must not affect vision-bridge selection or image
input handling. A route that can generate images but cannot understand image
input does not become vision-capable. A dual-role route needs the existing
vision/modalities metadata separately if it also accepts image input.
packages/core/src/models/types.ts
supportsImageGeneration?: boolean to ModelConfig and
AvailableModel.packages/core/src/models/ and export it through the existing core model
exports so CLI code does not duplicate the compatibility predicate.packages/core/src/models/modelRegistry.ts
supportsImageGeneration into AvailableModel.!model.imageOnly.packages/core/src/providers/types.ts
imageOnly.packages/core/src/providers/provider-config.ts
supportsImageGeneration: true when constructing provider model
templates.False and absent values may remain omitted, matching the existing compact provider-template style.
packages/core/src/config/config.ts
model.imageOnly === true image-generation eligibility check
in resolveImageGenerationModel() with the shared predicate.imageOnly unchanged.packages/cli/src/ui/commands/modelCommand.ts
/model --image <id> matching.imageOnly unchanged.packages/cli/src/ui/components/ModelDialog.tsx
imageOnly unchanged.packages/cli/src/config/settingsSchema.ts
imageModel description to require an image-generation-capable
model rather than specifically an imageOnly model.docs/users/configuration/model-providers.md
docs/users/configuration/settings.md
imageModel requirement.docs/users/features/commands.md
/model --image as selecting an image-generation-capable model.packages/vscode-ide-companion/schemas/settings.schema.json with
npm run generate:settings-schema rather than editing it manually./model --image. The locale
catalogs contain no translated imageOnly requirement, so they do not change.modelProviders is intentionally an open object in the generated schema. The
schema diff therefore updates only the imageModel description; it does not add
field-level provider metadata.
imageOnly read-site auditEvery current imageOnly read must be classified before editing it.
/model --image filtering and exact-match selection.imageOnly restrictionChanging a restriction read to the new capability predicate would recreate the bug in reverse by excluding dual-role routes from normal conversation.
AvailableModel values returned to CLI, ACP, daemon, and UI consumers.No settings migration is required.
imageOnly: true configurations remain selectable for image
generation and remain excluded from primary selection.imageModel selectors continue to resolve.supportsImageGeneration will not expose image_gen on those
versions, while legacy imageOnly routes continue to work.Because old runtimes ignore the new field, downstream platforms must gate use of dual-role configuration on a Qwen Code version that includes this change.
Add focused coverage for the shared predicate:
supportsImageGeneration: true returns true;imageOnly: true returns true for compatibility;imageOnly: true returns true.Extend model-registry tests:
AvailableModel;Extend config tests:
imageModel;image_gen;imageOnly: true still resolves;Extend provider-config tests to prove the field survives template expansion.
Extend /model command tests:
/model <dual-role-id> succeeds;/model --image <dual-role-id> succeeds and persists imageModel;supportsImageGeneration.Extend model-dialog tests with the same dual-role and image-only matrix.
Run:
npm run generate:settings-schema
Review the generated schema diff to confirm only the intended description change is present.
Run tests from their package directories as required by the repository:
cd packages/core && npx vitest run \
src/models/image-generation-capability.test.ts \
src/models/modelRegistry.test.ts \
src/models/modelsConfig.test.ts \
src/config/config.test.ts \
src/providers/__tests__/provider-config.test.ts
cd packages/cli && npx vitest run \
src/ui/commands/modelCommand.test.ts \
src/ui/components/ModelDialog.test.tsx \
src/config/settingsSchema.test.ts
Then run:
npm run build
npm run typecheck
Using a test endpoint that supports the existing image-generation protocol:
supportsImageGeneration: true and without
imageOnly./model --image.image_gen..qwen/generated-images/<session-id>/ and emitted as an image artifact.imageOnly: true route and confirm it remains rejected
as the primary model.supportsImageGeneration: true and no imageOnly restriction
can be selected both as the primary model and as imageModel.image_gen tool registers and executes for that route when all
existing endpoint, credential, mode, and permission checks pass.imageOnly: true remains unavailable as a primary model.supportsImageGeneration is true.imageOnly: true is
the only way to configure image generation.imageOnly read site against the classification in this
document before submitting the PR.Use one focused PR with a title such as:
feat(models): support dual-role image generation models
The PR should not include endpoint overrides, provider-specific configuration, automatic image-model selection, or unrelated model-capability cleanup. Those would widen the compatibility surface and obscure the selector-semantics fix.