fix(core): apply defaultModalities() on env-var-only model config (#4219)

When qwen-code is configured only via env vars (OPENAI_API_KEY /
OPENAI_BASE_URL / OPENAI_MODEL) with no modelProviders entry,
resolveGenerationConfig() never invoked defaultModalities(), so
generationConfig.modalities stayed undefined for image-capable
models. The two other config paths (modelRegistry.resolveModelConfig
and modelsConfig.applyResolvedModelDefaults) already call it. This
aligns the env-var-only path with both so multimodal models like
qwen3.6-35b-a3b correctly accept @image attachments.

Fixes #4219
This commit is contained in:
LaZzyMan 2026-05-18 11:05:27 +08:00
parent 4ab20ff6b8
commit 6f4a824612
2 changed files with 46 additions and 1 deletions

View file

@ -854,4 +854,38 @@ describe('modelConfigResolver', () => {
}
});
});
describe('[Regression] issue-4219 — env-var-only path must call defaultModalities()', () => {
it('[Regression] env-var-only path: modalities auto-detected for qwen3.6-35b-a3b', () => {
// REPRODUCES issue-4219:
// When the model is supplied only via OPENAI_MODEL (no modelProviders entry),
// resolveGenerationConfig() iterates MODEL_GENERATION_CONFIG_FIELDS but never
// calls defaultModalities(). This leaves config.modalities undefined, causing
// image attachments to be silently dropped with an "Unsupported <modality>"
// message even though the model supports images.
//
// The modelRegistry path (resolveModelConfig -> resolveModelConfig in modelRegistry.ts)
// and the modelsConfig path (applyResolvedModelDefaults) both call defaultModalities()
// when generationConfig.modalities is undefined. The env-var-only path does not.
const result = resolveModelConfig({
authType: AuthType.USE_OPENAI,
cli: {},
settings: {},
env: {
OPENAI_API_KEY: 'test-key',
OPENAI_BASE_URL: 'http://localhost:8000/v1',
OPENAI_MODEL: 'qwen3.6-35b-a3b',
},
// No modelProvider — this is the env-var-only path
});
expect(result.config.model).toBe('qwen3.6-35b-a3b');
// The qwen3.6-35b pattern in modalityDefaults.ts maps to { image: true, video: true }.
// The env-var-only path must auto-detect this — just as the modelProviders path does
// in modelsConfig.ts applyResolvedModelDefaults() lines 791-797.
expect(result.config.modalities).toBeDefined();
expect(result.config.modalities?.image).toBe(true);
});
});
});

View file

@ -21,6 +21,7 @@
import { AuthType } from '../core/contentGenerator.js';
import type { ContentGeneratorConfig } from '../core/contentGenerator.js';
import { DEFAULT_QWEN_MODEL } from '../config/models.js';
import { defaultModalities } from '../core/modalityDefaults.js';
import {
resolveField,
resolveOptionalField,
@ -268,7 +269,7 @@ export function resolveModelConfig(
settings?.generationConfig,
modelProvider?.generationConfig,
authType,
modelProvider?.id,
modelProvider?.id ?? modelResult.value,
sources,
);
@ -396,5 +397,15 @@ function resolveGenerationConfig(
}
}
// modalities fallback: auto-detect from model when neither modelProvider nor
// settings supplied it. Mirrors modelRegistry.resolveModelConfig and
// modelsConfig.applyResolvedModelDefaults so all paths agree on which models
// are multimodal — without this, env-var-only setups silently drop @image
// attachments for image-capable models (issue #4219).
if (result.modalities === undefined && modelId) {
result.modalities = defaultModalities(modelId);
sources['modalities'] = computedSource('auto-detected from model');
}
return result;
}