magi-code 0.63.4

Repository-aware CLI coding agent for terminal work
Documentation
use super::super::*;
use super::MissionControlApp;

impl MissionControlApp {
    pub(crate) fn select_model_from_text(
        &mut self,
        model_id: &str,
        ui_state: &mut state::MissionControlState,
    ) {
        let disabled = match crate::config::read_settings(&self.config.paths) {
            Ok(settings) => crate::config::disabled_model_ids_from_settings(&settings),
            Err(error) => {
                ui_state.status = format!("failed to read model settings: {error}");
                return;
            }
        };
        if disabled.contains(model_id) {
            ui_state.status = format!("cannot select disabled model: {model_id}");
            return;
        }
        self.select_model(model_id, ui_state);
    }

    pub(crate) fn select_model(
        &mut self,
        model_id: &str,
        ui_state: &mut state::MissionControlState,
    ) {
        match crate::commands::runtime::switch_model_selection(
            &self.config.paths,
            &self.state.auth_state,
            model_id,
        ) {
            Ok(selection) => {
                let provider = selection.provider;
                let model = selection.model;
                let notice = selection.notice;
                self.state.model = model.clone();
                if let Ok(refreshed) = crate::config::load_effective_provider_selection(
                    &self.config.paths,
                    &provider,
                    &model,
                ) {
                    self.state.auth_state = refreshed.auth_state();
                    self.state.config = Some(refreshed.clone());
                    self.config.provider = refreshed.provider.clone();
                    self.config.model = refreshed.model.clone();
                    self.config.auth = refreshed.auth.clone();
                    self.config.api_key = refreshed.api_key.clone();
                    self.config.custom_providers = refreshed.custom_providers.clone();
                    self.config.thinking_level = refreshed.thinking_level;
                }
                ui_state.provider = provider.clone();
                ui_state.provider_ready = self.state.auth_state.is_ready();
                ui_state.model = model.clone();
                ui_state.thinking_levels = cached_thinking_levels_for_model(
                    &self.config.paths,
                    &provider,
                    &model,
                    crate::thinking::capability_scope_for_provider(
                        &self.config.custom_providers,
                        &provider,
                    ),
                );
                ui_state.refresh_thinking_levels(
                    self.config.thinking_level,
                    ui_state.thinking_levels.clone(),
                );
                let context_window = crate::config::load_context_budget(&self.config)
                    .map(|budget| budget.max_tokens)
                    .unwrap_or_else(|_| crate::context::ContextBudget::default().max_tokens);
                ui_state.update_context_window(context_window);
                ui_state.status = format!("model switched to {provider}/{model}");
                if let Some(notice) = notice {
                    ui_state.status.push_str(&format!(" ({notice})"));
                }
            }
            Err(error) => {
                let message = error.to_string();
                let _ = record_session_event(
                    self.state.current_session.as_ref(),
                    &self.state.cwd,
                    SessionEventKind::Diagnostic,
                    serde_json::json!({"level":"error", "message": message}),
                );
                let _ = send_critical(&self.events, TuiEvent::Error(message));
            }
        }
    }

    pub(crate) fn open_model_picker(
        &mut self,
        ui_state: &mut state::MissionControlState,
        terminal_area: ratatui::layout::Rect,
    ) {
        if let Some(catalog) = &self.model_catalog_cache
            && catalog.notice.is_none()
        {
            let disabled = crate::config::read_settings(&self.config.paths)
                .map(|settings| crate::config::disabled_model_ids_from_settings(&settings))
                .unwrap_or_default();
            ui_state.open_model_picker(
                catalog.entries.clone(),
                disabled,
                catalog.notice.clone(),
                model_picker_visible_rows(terminal_area, catalog.notice.is_some()),
            );
            ui_state.status = "model catalog loaded".to_string();
            return;
        }
        ui_state.status = "loading model catalogs…".to_string();
        self.model_catalog_loading = true;
        self.pending_model_catalog_consumer =
            Some(super::PendingModelCatalogConsumer::SetModelPicker);
        self.next_model_catalog_request_id += 1;
        let request_id = self.next_model_catalog_request_id;
        ui_state.pending_model_catalog_request_id = Some(request_id);
        let paths = self.config.paths.clone();
        let sender = self.events.clone();
        // fire-and-forget: model catalog load is short-lived; completion returns through TUI channel with request id.
        thread::spawn(move || {
            let agg = crate::model_catalog::load_aggregated_catalog(
                &paths,
                crate::model_catalog::CachePreference::AllowStale,
            );
            let notice = if agg.notices.is_empty() {
                None
            } else {
                Some(agg.notices.join("; "))
            };
            let visible_rows = model_picker_visible_rows(terminal_area, notice.is_some());
            let result = Ok(crate::model_catalog::CatalogForUi {
                entries: agg.entries,
                stale: agg.stale,
                notice,
            });
            let _ = send_critical(
                &sender,
                TuiEvent::ModelCatalog {
                    request_id,
                    result,
                    visible_rows,
                },
            );
        });
    }
}