1```mermaid
  2sequenceDiagram
  3    participant UI as ๐Ÿงฉ ModelsSelector
  4    participant Hooks as ๐Ÿช useModelChangeValidation
  5    participant modelsStore as ๐Ÿ—„๏ธ modelsStore
  6    participant serverStore as ๐Ÿ—„๏ธ serverStore
  7    participant convStore as ๐Ÿ—„๏ธ conversationsStore
  8    participant ModelsSvc as โš™๏ธ ModelsService
  9    participant PropsSvc as โš™๏ธ PropsService
 10    participant API as ๐ŸŒ llama-server
 11
 12    Note over modelsStore: State:<br/>models: ModelOption[]<br/>routerModels: ApiModelDataEntry[]<br/>selectedModelId, selectedModelName<br/>loading, updating, error<br/>modelLoadingStates (Map)<br/>modelPropsCache (Map)<br/>propsCacheVersion
 13
 14    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 15    Note over UI,API: ๐Ÿš€ INITIALIZATION (MODEL mode)
 16    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 17
 18    UI->>modelsStore: fetch()
 19    activate modelsStore
 20    modelsStore->>modelsStore: loading = true
 21
 22    alt serverStore.props not loaded
 23        modelsStore->>serverStore: fetch()
 24        Note over serverStore: โ†’ see server-flow.mmd
 25    end
 26
 27    modelsStore->>ModelsSvc: list()
 28    ModelsSvc->>API: GET /v1/models
 29    API-->>ModelsSvc: ApiModelListResponse {data: [model]}
 30
 31    modelsStore->>modelsStore: models = $state(mapped)
 32    Note right of modelsStore: Map to ModelOption[]:<br/>{id, name, model, description, capabilities}
 33
 34    Note over modelsStore: MODEL mode: Get modalities from serverStore.props
 35    modelsStore->>modelsStore: modelPropsCache.set(model.id, serverStore.props)
 36    modelsStore->>modelsStore: models[0].modalities = props.modalities
 37
 38    modelsStore->>modelsStore: Auto-select single model
 39    Note right of modelsStore: selectedModelId = models[0].id
 40    modelsStore->>modelsStore: loading = false
 41    deactivate modelsStore
 42
 43    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 44    Note over UI,API: ๐Ÿš€ INITIALIZATION (ROUTER mode)
 45    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 46
 47    UI->>modelsStore: fetch()
 48    activate modelsStore
 49    modelsStore->>ModelsSvc: list()
 50    ModelsSvc->>API: GET /v1/models
 51    API-->>ModelsSvc: ApiModelListResponse
 52    modelsStore->>modelsStore: models = $state(mapped)
 53    deactivate modelsStore
 54
 55    Note over UI: After models loaded, layout triggers:
 56    UI->>modelsStore: fetchRouterModels()
 57    activate modelsStore
 58    modelsStore->>ModelsSvc: listRouter()
 59    ModelsSvc->>API: GET /v1/models
 60    API-->>ModelsSvc: ApiRouterModelsListResponse
 61    Note right of API: {data: [{id, status, path, in_cache}]}
 62    modelsStore->>modelsStore: routerModels = $state(data)
 63
 64    modelsStore->>modelsStore: fetchModalitiesForLoadedModels()
 65    loop each model where status === "loaded"
 66        modelsStore->>PropsSvc: fetchForModel(modelId)
 67        PropsSvc->>API: GET /props?model={modelId}
 68        API-->>PropsSvc: ApiLlamaCppServerProps
 69        modelsStore->>modelsStore: modelPropsCache.set(modelId, props)
 70    end
 71    modelsStore->>modelsStore: propsCacheVersion++
 72    deactivate modelsStore
 73
 74    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 75    Note over UI,API: ๐Ÿ”„ MODEL SELECTION (ROUTER mode)
 76    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 77
 78    UI->>Hooks: useModelChangeValidation({getRequiredModalities, onSuccess?, onValidationFailure?})
 79    Note over Hooks: Hook configured per-component:<br/>ChatForm: getRequiredModalities = usedModalities<br/>ChatMessage: getRequiredModalities = getModalitiesUpToMessage(msgId)
 80
 81    UI->>Hooks: handleModelChange(modelId, modelName)
 82    activate Hooks
 83    Hooks->>Hooks: previousSelectedModelId = modelsStore.selectedModelId
 84    Hooks->>modelsStore: isModelLoaded(modelName)?
 85
 86    alt model NOT loaded
 87        Hooks->>modelsStore: loadModel(modelName)
 88        Note over modelsStore: โ†’ see LOAD MODEL section below
 89    end
 90
 91    Note over Hooks: Always fetch props (from cache or API)
 92    Hooks->>modelsStore: fetchModelProps(modelName)
 93    modelsStore-->>Hooks: props
 94
 95    Hooks->>convStore: getRequiredModalities()
 96    convStore-->>Hooks: {vision, audio}
 97
 98    Hooks->>Hooks: Validate: model.modalities โЇ required?
 99
100    alt validation PASSED
101        Hooks->>modelsStore: selectModelById(modelId)
102        Hooks-->>UI: return true
103    else validation FAILED
104        Hooks->>UI: toast.error("Model doesn't support required modalities")
105        alt model was just loaded
106            Hooks->>modelsStore: unloadModel(modelName)
107        end
108        alt onValidationFailure provided
109            Hooks->>modelsStore: selectModelById(previousSelectedModelId)
110        end
111        Hooks-->>UI: return false
112    end
113    deactivate Hooks
114
115    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
116    Note over UI,API: โฌ†๏ธ LOAD MODEL (ROUTER mode)
117    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
118
119    modelsStore->>modelsStore: loadModel(modelId)
120    activate modelsStore
121
122    alt already loaded
123        modelsStore-->>modelsStore: return (no-op)
124    end
125
126    modelsStore->>modelsStore: modelLoadingStates.set(modelId, true)
127    modelsStore->>ModelsSvc: load(modelId)
128    ModelsSvc->>API: POST /models/load {model: modelId}
129    API-->>ModelsSvc: {status: "loading"}
130
131    modelsStore->>modelsStore: pollForModelStatus(modelId, LOADED)
132    loop poll every 500ms (max 60 attempts)
133        modelsStore->>modelsStore: fetchRouterModels()
134        modelsStore->>ModelsSvc: listRouter()
135        ModelsSvc->>API: GET /v1/models
136        API-->>ModelsSvc: models[]
137        modelsStore->>modelsStore: getModelStatus(modelId)
138        alt status === LOADED
139            Note right of modelsStore: break loop
140        else status === LOADING
141            Note right of modelsStore: wait 500ms, continue
142        end
143    end
144
145    modelsStore->>modelsStore: updateModelModalities(modelId)
146    modelsStore->>PropsSvc: fetchForModel(modelId)
147    PropsSvc->>API: GET /props?model={modelId}
148    API-->>PropsSvc: props with modalities
149    modelsStore->>modelsStore: modelPropsCache.set(modelId, props)
150    modelsStore->>modelsStore: propsCacheVersion++
151
152    modelsStore->>modelsStore: modelLoadingStates.set(modelId, false)
153    deactivate modelsStore
154
155    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
156    Note over UI,API: โฌ‡๏ธ UNLOAD MODEL (ROUTER mode)
157    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
158
159    modelsStore->>modelsStore: unloadModel(modelId)
160    activate modelsStore
161    modelsStore->>modelsStore: modelLoadingStates.set(modelId, true)
162    modelsStore->>ModelsSvc: unload(modelId)
163    ModelsSvc->>API: POST /models/unload {model: modelId}
164
165    modelsStore->>modelsStore: pollForModelStatus(modelId, UNLOADED)
166    loop poll until unloaded
167        modelsStore->>ModelsSvc: listRouter()
168        ModelsSvc->>API: GET /v1/models
169    end
170
171    modelsStore->>modelsStore: modelLoadingStates.set(modelId, false)
172    deactivate modelsStore
173
174    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
175    Note over UI,API: ๐Ÿ“Š COMPUTED GETTERS
176    %% โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
177
178    Note over modelsStore: Getters:<br/>- selectedModel: ModelOption | null<br/>- loadedModelIds: string[] (from routerModels)<br/>- loadingModelIds: string[] (from modelLoadingStates)<br/>- singleModelName: string | null (MODEL mode only)
179
180    Note over modelsStore: Modality helpers:<br/>- getModelModalities(modelId): {vision, audio}<br/>- modelSupportsVision(modelId): boolean<br/>- modelSupportsAudio(modelId): boolean
181```