Skip to content

feat: Add /model slash command for changing models - #3576

Merged
ellie merged 3 commits into
mainfrom
mkt/switch-model
Jul 7, 2026
Merged

feat: Add /model slash command for changing models#3576
ellie merged 3 commits into
mainfrom
mkt/switch-model

Conversation

@BinaryMuse

@BinaryMuse BinaryMuse commented Jul 7, 2026

Copy link
Copy Markdown
Member

This PR adds the /model slash command to Atuin AI. This fetches https://hub.atuin.sh/api/cli/models and presents the options in a pick list, caching the HTTP response for the lifetime of the program.

Selecting a model sets it as the model for that session as well as updates ai.model in config.toml.

image

Additional UI changes to display the selected model, usage information, etc. will come in a future PR.

@BinaryMuse
BinaryMuse requested review from ellie and taylordotfish July 7, 2026 21:16
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a /model slash command that fetches the available model list from hub.atuin.sh/api/cli/models, presents it in a pick list, and persists the selection to ai.model in config.toml. The model is applied to all subsequent chat requests for the session.

  • FSM gains ModelPicker (Loading/Ready) state, a per-invocation models_cache, and three new events (OpenModelPicker, ModelListLoaded, ModelSelected), with solid test coverage for cache reuse, dismissal, and session survival.
  • ATUIN_AI__MODEL env-var support is preserved transparently: the old in-stream read is removed, but the settings layer already maps ATUIN_AI__MODELai.model via its ATUIN_/__ prefix/separator convention.

Confidence Score: 4/5

Ready to merge; the only rough edge is a fire-and-forget config write that can silently fail without telling the user.

The FSM logic, caching, and view rendering are all well-structured and covered by tests. The one thing that could surprise a user is that the 'Model set for future sessions' confirmation fires before the config write completes — if the write fails, they won't know.

crates/atuin-ai/src/driver.rs — the SaveModelSelection effect handler silently swallows write errors after the user has already seen a success message.

Important Files Changed

Filename Overview
crates/atuin-ai/src/models.rs New module: authenticated model-list fetch and TOML config persistence; read-modify-write without a tmp-file swap but failures are acceptable given the use case
crates/atuin-ai/src/fsm/mod.rs FSM extended with model-picker states and transitions; cache-reuse, Esc-dismiss, and post-dismiss list-arrival are all handled correctly
crates/atuin-ai/src/stream.rs Replaced direct env-var ATUIN_AI__MODEL read with the model field on ChatRequest; env-var still works via the settings layer (ATUIN_ prefix + __ separator)
crates/atuin-ai/src/driver.rs Model-picker events wired up; save failure is fire-and-forget with no user-visible error path
crates/atuin-ai/src/tui/view/mod.rs View correctly gates picker vs. spinner vs. input-box rendering; model_picker_view marks the active model
crates/atuin-ai/src/fsm/tests.rs Good test coverage for all new FSM transitions including cache reuse, Esc-during-load, and model survival across NewSession
crates/atuin-client/src/settings.rs Added optional ai.model field; clean addition with no migrations needed
crates/atuin-ai/src/commands/inline.rs Seeds fsm.ctx.model from settings at startup after the FSM is built; ordering is correct relative to build_view_state
crates/atuin-ai/src/tui/slash.rs Registers /model in the slash command registry

Reviews (1): Last reviewed commit: "Merge branch 'main' into mkt/switch-mode..." | Re-trigger Greptile

Comment thread crates/atuin-ai/src/driver.rs
Comment on lines +53 to +70
/// Persist the chosen alias to `ai.model` in config.toml so it becomes the
/// default for future sessions. Already-running sessions keep the model they
/// read at startup.
pub(crate) async fn save_model_selection(alias: &str) -> Result<()> {
let config_file = atuin_client::settings::Settings::get_config_path()?;
let config_str = tokio::fs::read_to_string(&config_file)
.await
.unwrap_or_default();
let mut doc = config_str.parse::<toml_edit::DocumentMut>()?;

if !doc.contains_key("ai") {
doc["ai"] = toml_edit::table();
}
doc["ai"]["model"] = toml_edit::value(alias);

tokio::fs::write(&config_file, doc.to_string()).await?;
Ok(())
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will refactor crates/atuin/src/command/client/config.rs in a future PR to remove this duplication.

@ellie ellie self-assigned this Jul 7, 2026
@ellie
ellie merged commit a96e6c7 into main Jul 7, 2026
25 checks passed
@ellie
ellie deleted the mkt/switch-model branch July 7, 2026 23:15
taylordotfish pushed a commit that referenced this pull request Jul 9, 2026
### Bug Fixes

- *(ai)* Dispatch skills registered in the slash command registry
([#3593](#3593))
- *(ci)* Fossier install in scan workflow
([#3485](#3485))
- *(i18n)* Fix typos in Russian localization
([#3575](#3575))
- *(nu)* Use `char -u 1b` for ESC in OSC 133 sequences
([#3530](#3530))
- *(nu)* Suppress error when `ATUIN_HISTORY_ID` is missing in
`pre_prompt` hook
([#3587](#3587))
- *(pi)* Observe tool events instead of registering a bash tool
([#3557](#3557))
- *(pty-proxy)* Set `$SHELL` to the spawned shell
([#3548](#3548))
- *(search)* Fix terminal clearing with latest Ratatui
([#3578](#3578))
- *(sync)* Skip records that fail to decrypt or decode instead of
failing the whole store
([#3569](#3569))
- Atuin hangs when attempting to spawn daemon from Ctrl+R invocation
([#3502](#3502))
- Capture session ID from stream headers rather than final event
([#3531](#3531))
- Doctor resiliency fo runknown platforms + openbsd warning
([#3551](#3551))
- Double input on arrow keys in AI setup prompt on Windows
([#3552](#3552))
- Exclude AI agent commands from zsh-autosuggestions
([#3567](#3567))
- Silence shellcheck SC2016 on literal `$all-user` author filter
- Respect `store_failed` when using daemon
([#3571](#3571))


### Documentation

- Highlight `Ctrl-r` keybinding on docs page
([#3489](#3489))
- Document store purge workflow
([#3544](#3544))
- Fix command example typo in documentation
([#3536](#3536))
- Make commented-out lines in `config.toml` match real defaults
([#3583](#3583))
- Add fish shell cleanup step to uninstall instructions
([#3582](#3582))


### Features

- *(doctor)* Add whether daemon is enabled to `doctor` output
([#3572](#3572))
- *(pty-proxy)* Add `--shell` flag to override the spawned shell
([#3327](#3327))
- Setup fossier to stop bot slop prs
([#3482](#3482))
- Capture command output + expose to new `atuin_output` tool
([#3510](#3510))
- Cache user contexts on load until `/reload`
([#3525](#3525))
- Create database integration tests for atuin-server
([#3514](#3514))
- Add `/model` slash command for changing models
([#3576](#3576))
- Add mcp server for history tools and expand search filters
([#3581](#3581))
- Add status bar with model and usage information
([#3591](#3591))


### Miscellaneous Tasks

- *(rustdoc)* Fix Rustdoc warnings
([#3585](#3585))
- *(warnings)* Fix compile warnings with latest dependencies
([#3586](#3586))
- Vouch for all existing contributors
([#3486](#3486))
- Update GitHub app token format
- Update to Rust 1.96.1
([#3568](#3568))
- Adopt `derive_more` to reduce boilerplate across the codebase
([#3573](#3573))


### Performance

- *(search)* Scan history by recency until N unique
([#3553](#3553))
- Add `synchronous(Normal)` + `optimize_on_close` to record store SQLite
([#3577](#3577))
- Remove unnecessary clones in a hot path
([#3580](#3580))


### Refactor

- Implement `From<sqlx::Error>` and clean up `fix_error`
([#3484](#3484))
- Pull `fn into_utc` into `atuin-server-database` crate
([#3487](#3487))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants