{% from "_macros.html" import icon, model_avatar %} {# Model picker. A real dropdown rather than a , because a cannot show an image or an icon -- browsers render only text in an . The hidden input is what actually carries the value, so the control still behaves like a form field. Each option is name, context window and an eye for vision, and nothing else. It listed every capability switch as a tag until 1.8.2, which is twenty `tool_*` tags per model in a menu whose one job is choosing; the full list is on /settings. The options share the list's column tracks (subgrid), so the context sizes and the eyes line up whatever a name's length -- and every option emits every slot, empty or not, or its row shifts. Inside a chat it PATCHes the chat; on /chat it navigates, because there is no chat row to patch yet. #} {% if current_model %} {{ model_avatar(current_model, cls="picker__avatar") }} {{ current_model.label }} {% else %} {{ t("Choose a model") }} {% endif %} {{ icon("chevron-down", "icon--sm picker__chevron") }} {% if models|length > 8 %} {% endif %} {% for model in models %} {{ model_avatar(model, cls="picker__avatar") }} {{ model.label }} {% if model.pinned %}{{ icon("pin", "icon--sm picker__pin") }}{% endif %} {%- if model.context_length %}CTX {{ model.context_length|context_size }}{% endif -%} {%- if (model.capabilities_json or {}).get("vision") -%} {{ icon("eye", "icon--sm") }}{{ t("Sees images") }} {%- endif -%} {# Always rendered and shown by `.is-selected`, so it follows ui.js's in-place choice rather than staying on the model the page loaded with. #} {{ icon("check", "icon--sm") }} {% endfor %} {{ t("No model matches that.") }} {% if chat %} {# Changing the value fires the PATCH; htmx serialises the hidden input. #} {% else %} {# No chat yet: selecting navigates so the whole composer re-renders with the right vision warning and the right hidden model_id. #} {% endif %}
{{ t("No model matches that.") }}