From 0c65ed33bc4b7b4186c292d039a5af84ebaca884 Mon Sep 17 00:00:00 2001 From: Michael Verrilli Date: Tue, 21 Apr 2026 13:37:36 -0500 Subject: [PATCH] cmd: populate model capabilities in launchInteractiveModel (#15712) launchInteractiveModel was introduced in PR #14609 without the client.Show() capability-detection block that RunHandler uses. This left opts.MultiModal always false in the TUI path, causing image/audio file paths to always be treated as unknown commands instead of being loaded as multimodal attachments. Mirror the Show() call, pull-on-404 fallback, cloud auth handling, and MultiModal/Think population from RunHandler into launchInteractiveModel. Fixes #15711 --- cmd/cmd.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 43a2e7d3..e7bfb28e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1975,8 +1975,61 @@ func launchInteractiveModel(cmd *cobra.Command, modelName string) error { Options: map[string]any{}, ShowConnect: true, } - // loadOrUnloadModel is cloud-safe here: remote/cloud models skip local preload - // and only validate auth/connectivity before interactive chat starts. + + client, err := api.ClientFromEnvironment() + if err != nil { + return err + } + + requestedCloud := modelref.HasExplicitCloudSource(modelName) + + info, err := func() (*api.ShowResponse, error) { + showReq := &api.ShowRequest{Name: modelName} + info, err := client.Show(cmd.Context(), showReq) + var se api.StatusError + if errors.As(err, &se) && se.StatusCode == http.StatusNotFound { + if requestedCloud { + return nil, err + } + if err := PullHandler(cmd, []string{modelName}); err != nil { + return nil, err + } + return client.Show(cmd.Context(), &api.ShowRequest{Name: modelName}) + } + return info, err + }() + if err != nil { + if handleCloudAuthorizationError(err) { + return nil + } + return err + } + + ensureCloudStub(cmd.Context(), client, modelName) + + opts.Think, err = inferThinkingOption(&info.Capabilities, &opts, false) + if err != nil { + return err + } + + audioCapable := slices.Contains(info.Capabilities, model.CapabilityAudio) + opts.MultiModal = slices.Contains(info.Capabilities, model.CapabilityVision) || audioCapable + + // TODO: remove the projector info and vision info checks below, + // these are left in for backwards compatibility with older servers + // that don't have the capabilities field in the model info + if len(info.ProjectorInfo) != 0 { + opts.MultiModal = true + } + for k := range info.ModelInfo { + if strings.Contains(k, ".vision.") { + opts.MultiModal = true + break + } + } + + applyShowResponseToRunOptions(&opts, info) + if err := loadOrUnloadModel(cmd, &opts); err != nil { return fmt.Errorf("error loading model: %w", err) }