-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(web-shell): add daemon UI support for vision model selection #6209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
01dcde7
9cd45db
f6c1a9b
c530fd2
bd4c5b6
c8f02d7
d95a0ea
6e55ae6
ac7f851
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ package-lock.json | |
| .claude | ||
| .codex | ||
| .worktrees/ | ||
| .atlarix/ | ||
|
|
||
| # Qwen Code Configs | ||
| .qwen/* | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2430,6 +2430,21 @@ export function App({ | |
| ); | ||
| return true; | ||
| } | ||
| if (modelArg === '--vision') { | ||
| setModelDialogMode('vision'); | ||
| return true; | ||
| } | ||
| if (modelArg.startsWith('--vision ')) { | ||
| const visionModelId = modelArg.replace(/^--vision\s+/, ''); | ||
| setWorkspaceSetting( | ||
| 'workspace', | ||
| 'visionModel', | ||
| visionModelId, | ||
| ).catch((error: unknown) => | ||
| reportError(error, t('model.setVision')), | ||
| ); | ||
| return true; | ||
| } | ||
| if (modelArg) { | ||
| if (!connectionRef.current.sessionId) { | ||
| setPendingModel(modelArg); | ||
|
|
@@ -3298,6 +3313,15 @@ export function App({ | |
| [reportError, setWorkspaceSetting, t], | ||
| ); | ||
|
|
||
| const handleVisionModelSelect = useCallback( | ||
| (modelId: string) => { | ||
| setWorkspaceSetting('workspace', 'visionModel', modelId).catch( | ||
| (error: unknown) => reportError(error, t('model.setVision')), | ||
| ); | ||
| }, | ||
| [reportError, setWorkspaceSetting, t], | ||
| ); | ||
|
|
||
| const commands = useMemo(() => { | ||
| const skillNames = new Set(connection.skills ?? []); | ||
| return mergeCommands(connection.commands ?? [], getLocalCommands(t)) | ||
|
|
@@ -3437,7 +3461,9 @@ export function App({ | |
| ? t('model.setFast') | ||
| : modelDialogMode === 'voice' | ||
| ? t('model.setVoice') | ||
| : t('model.select') | ||
| : modelDialogMode === 'vision' | ||
| ? t('model.setVision') | ||
| : t('model.select') | ||
| } | ||
| size="lg" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Dialog title ternary duplicated + onSelect handler not exhaustive This 4-way ternary (main → fast → voice → vision → default) is duplicated in Separately, the Consider extracting shared lookups: const MODE_TITLE_KEY: Record<ModelDialogMode, string> = {
main: 'model.select',
fast: 'model.setFast',
voice: 'model.setVoice',
vision: 'model.setVision',
};
// In onSelect:
const handlers: Record<ModelDialogMode, (id: string) => void> = {
main: handleModelSelect,
fast: handleFastModelSelect,
voice: handleVoiceModelSelect,
vision: handleVisionModelSelect,
};
handlers[modelDialogMode ?? 'main'](modelId);— qwen3.7-max via Qwen Code /review |
||
| onClose={() => setModelDialogMode(null)} | ||
|
|
@@ -3453,6 +3479,8 @@ export function App({ | |
| handleFastModelSelect(modelId); | ||
| } else if (modelDialogMode === 'voice') { | ||
| handleVoiceModelSelect(modelId); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing The Derive const currentVisionModel = (() => {
const value = workspaceSettings.find(
(setting) => setting.key === 'visionModel',
)?.values.effective;
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
})();Then extend the prop: currentModelId={
modelDialogMode === 'voice' ? currentVoiceModel
: modelDialogMode === 'vision' ? currentVisionModel
: undefined
}— qwen3.7-max via Qwen Code /review |
||
| } else if (modelDialogMode === 'vision') { | ||
| handleVisionModelSelect(modelId); | ||
| } else { | ||
| handleModelSelect(modelId); | ||
| } | ||
|
|
@@ -3556,6 +3584,7 @@ export function App({ | |
| onSubDialog={(key) => { | ||
| setShowSettingsDialog(false); | ||
| if (key === 'fastModel') setModelDialogMode('fast'); | ||
| else if (key === 'visionModel') setModelDialogMode('vision'); | ||
| else if (key === 'tools.approvalMode') | ||
| setShowApprovalModeDialog(true); | ||
| }} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -962,6 +962,7 @@ const EN: Messages = { | |
| 'model.select': 'Select Model', | ||
| 'model.setFast': 'Set Fast Model', | ||
| 'model.setVoice': 'Set Voice Model', | ||
| 'model.setVision': 'Set Vision Model', | ||
| 'model.switch': 'Switch Model', | ||
| 'model.unknown': 'unknown', | ||
| 'resume.current': 'current', | ||
|
|
@@ -2143,6 +2144,7 @@ const ZH: Messages = { | |
| 'model.select': '选择模型', | ||
| 'model.setFast': '设置 Fast Model', | ||
| 'model.setVoice': '设置语音模型', | ||
| 'model.setVision': '设置视觉模型', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing The Add to both EN and ZH, mirroring the — qwen3.7-max via Qwen Code /review |
||
| 'model.switch': '切换模型', | ||
| 'model.unknown': '未知', | ||
| 'resume.current': '当前', | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Critical] Vision model stored as bare ID — core resolution expects provider-qualified encoding
Both this handler and the
/model --vision <id>inline path (line 2433) callsetWorkspaceSetting('workspace', 'visionModel', modelId)with a bare model ID. The CLI path usesencodeVisionModelSelector(selected)which producesauthType:modelId\0baseUrl(seepackages/cli/src/ui/components/ModelDialog.tsx:600).Core's
resolveVisionModelSelection()(packages/core/src/config/config.ts:3147) parses throughparseVisionModelSetting, then callsresolveModelId(parsedSetting.selector)which expects theauthType:modelIdformat. When it receives a bare ID,resolveModelIdcannot extract an authType qualifier — if the same model ID appears on multiple providers/endpoints (e.g.,gpt-4oon both OpenAI direct and Azure), the ambiguity guard at line 3187 silently drops the pin. The user sees a success toast but their choice is discarded.The CLI also calls
config?.setVisionModel(visionModel)after persisting to sync the runtime — the web-shell path skips this, so the pin doesn't take effect until the next Config rebuild.— qwen3.7-max via Qwen Code /review