@@ -68,6 +68,7 @@ import {
6868} from './app-shell/agent-options' ;
6969import { AgentOptionAvatar , AgentOptionRow } from './app-shell/components/AgentOptionAvatar' ;
7070import { AgentsIcon } from './app-shell/icons' ;
71+ import { resolveComposerModelControlState } from './composer-model-control' ;
7172
7273export type { ComposerAgentOption } ;
7374
@@ -419,101 +420,38 @@ function renderComposerModelControl({
419420 return null ;
420421 }
421422
422- const catalogModels = providerModels . models || [ ] ;
423- const effectiveModelId =
424- selectedModel ?. trim ( ) ||
425- effectiveModel ?. trim ( ) ||
426- agentConfiguredModel ?. trim ( ) ||
427- "" ;
428- // Include the running model even when the provider catalog omits it, so the
429- // picker can always mark the active row (mirrors the mobile thread sheet).
430- const models =
431- effectiveModelId && ! catalogModels . some ( ( option ) => option . id === effectiveModelId )
432- ? [
433- ...catalogModels ,
434- {
435- id : effectiveModelId ,
436- label : effectiveModelId ,
437- recommended : false ,
438- supportedReasoningEfforts : providerModels . reasoningEfforts || [ ] ,
439- serviceTiers : providerModels . serviceTiers || [ ] ,
440- } ,
441- ]
442- : catalogModels ;
443- const selectedModelOption = selectedModel
444- ? models . find ( ( option ) => option . id === selectedModel )
445- : undefined ;
446- const effectiveModelOption = effectiveModelId
447- ? models . find ( ( option ) => option . id === effectiveModelId )
448- : undefined ;
449- const defaultModelId =
450- agentConfiguredModel ?. trim ( ) || providerModels . defaultModel ?. trim ( ) || effectiveModelId ;
451- const defaultModelOption = defaultModelId
452- ? models . find ( ( option ) => option . id === defaultModelId )
453- : undefined ;
454- const defaultModelLabel = defaultModelOption ?. label || defaultModelId || t ( "Model" ) ;
455- // Thinking options follow the model that will actually run: explicit
456- // override first, then the resolved agent/provider default.
457- const effortFilterModelOption = selectedModelOption || effectiveModelOption ;
458- const catalogReasoningEfforts =
459- effortFilterModelOption ?. supportedReasoningEfforts ?. length
460- ? effortFilterModelOption . supportedReasoningEfforts
461- : providerModels . reasoningEfforts || [ ] ;
462- const effectiveReasoningEffortId =
463- selectedReasoningEffort ?. trim ( ) || effectiveReasoningEffort ?. trim ( ) || "" ;
464- // Include the running level even when the catalog omits it, so the picker can
465- // always mark the active row.
466- const reasoningEfforts =
467- effectiveReasoningEffortId &&
468- ! catalogReasoningEfforts . some ( ( option ) => option . id === effectiveReasoningEffortId )
469- ? [
470- ...catalogReasoningEfforts ,
471- { id : effectiveReasoningEffortId , label : effectiveReasoningEffortId , recommended : false } ,
472- ]
473- : catalogReasoningEfforts ;
423+ const {
424+ models,
425+ effectiveModelId,
426+ defaultModelId,
427+ defaultModelLabel,
428+ defaultModelOption,
429+ triggerLabel,
430+ reasoningEfforts,
431+ effectiveReasoningEffortId,
432+ defaultReasoningEffortId,
433+ defaultEffortLabel,
434+ serviceTiers,
435+ effectiveServiceTierId,
436+ defaultServiceTierLabel,
437+ } = resolveComposerModelControlState ( {
438+ providerModels,
439+ agentConfiguredModel,
440+ effectiveModel,
441+ effectiveReasoningEffort,
442+ effectiveServiceTier,
443+ selectedModel,
444+ selectedReasoningEffort,
445+ selectedServiceTier,
446+ modelFallbackLabel : t ( "Model" ) ,
447+ thinkingLevelFallbackLabel : t ( "Thinking level" ) ,
448+ standardServiceTierLabel : t ( "Standard" ) ,
449+ } ) ;
474450 const supportsReasoning =
475451 Boolean ( providerModels . supportsReasoningEffortSelection ) &&
476452 reasoningEfforts . length > 0 ;
477- const selectedEffortOption = effectiveReasoningEffortId
478- ? reasoningEfforts . find ( ( option ) => option . id === effectiveReasoningEffortId )
479- : undefined ;
480- const defaultReasoningEffortId =
481- effortFilterModelOption ?. defaultReasoningEffort ?. trim ( ) ||
482- reasoningEfforts . find ( ( option ) => option . recommended ) ?. id ||
483- reasoningEfforts [ 0 ] ?. id ||
484- "" ;
485- const defaultEffortOption = defaultReasoningEffortId
486- ? reasoningEfforts . find ( ( option ) => option . id === defaultReasoningEffortId )
487- : undefined ;
488- const defaultEffortLabel =
489- defaultEffortOption ?. label || defaultReasoningEffortId || t ( "Thinking level" ) ;
490- // Service tier (speed) follows the same model as thinking level.
491- const catalogServiceTiers =
492- effortFilterModelOption ?. serviceTiers ?. length
493- ? effortFilterModelOption . serviceTiers
494- : providerModels . serviceTiers || [ ] ;
495- const effectiveServiceTierId =
496- selectedServiceTier ?. trim ( ) || effectiveServiceTier ?. trim ( ) || "" ;
497- const serviceTiers =
498- effectiveServiceTierId &&
499- ! catalogServiceTiers . some ( ( option ) => option . id === effectiveServiceTierId )
500- ? [
501- ...catalogServiceTiers ,
502- { id : effectiveServiceTierId , label : effectiveServiceTierId , recommended : false } ,
503- ]
504- : catalogServiceTiers ;
505453 const supportsServiceTier =
506454 Boolean ( providerModels . supportsServiceTierSelection ) && serviceTiers . length > 0 ;
507- // The provider default tier is "standard" (no explicit tier); represented as
508- // clearing the override.
509- const defaultServiceTierLabel = t ( "Standard" ) ;
510- const triggerLabel = effectiveModelOption
511- ? selectedEffortOption
512- ? `${ effectiveModelOption . label } · ${ selectedEffortOption . label } `
513- : effectiveModelOption . label
514- : selectedEffortOption
515- ? `${ t ( "Model" ) } · ${ selectedEffortOption . label } `
516- : t ( "Model" ) ;
517455
518456 // Selecting a model also clears a service tier the target model does not
519457 // support, so the thread never runs with an unsupported speed tier (mirrors
@@ -539,7 +477,7 @@ function renderComposerModelControl({
539477 < DropdownMenuTrigger
540478 aria-label = { t ( "Change model for this thread" ) }
541479 className = "composer-provider-trigger"
542- data-muted = { ! effectiveModelOption && ! selectedEffortOption ? "" : undefined }
480+ data-muted = { ! effectiveModelId && ! effectiveReasoningEffortId ? "" : undefined }
543481 type = "button"
544482 >
545483 < IconCube aria-hidden size = { 15 } stroke = { 1.75 } />
0 commit comments