Skip to content

Add model switching to the lean TUI - #3674

Merged
rumpl merged 2 commits into
docker:mainfrom
rumpl:feat-lean-model-picker
Jul 16, 2026
Merged

rumpl merged 2 commits into
docker:mainfrom
rumpl:feat-lean-model-picker

Conversation

@rumpl

@rumpl rumpl commented Jul 15, 2026

Copy link
Copy Markdown
Member

Add /model support to the lean TUI and share fuzzy model search between the lean and full TUIs.

@rumpl
rumpl requested a review from a team as a code owner July 15, 2026 22:23

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Assessment: 🟡 NEEDS ATTENTION

One medium-severity issue found in the model picker filtering logic introduced by this PR.

Comment thread pkg/tui/dialog/model_picker.go Outdated
if isCustomQuery && !slices.ContainsFunc(d.models, func(model runtime.ModelChoice) bool {
return strings.EqualFold(model.Ref, query) || strings.EqualFold(model.Provider+"/"+model.Model, query)
}) {
d.filtered = []runtime.ModelChoice{{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Custom-model override replaces all fuzzy matches when query contains "/" but isn't an exact match

In filterModels(), the comment says "show Custom option as well as matches" but the code replaces d.filtered entirely instead of appending to it:

// A provider/model query is an explicit reference rather than a fuzzy
// search when it does not identify an available choice.
if isCustomQuery && !slices.ContainsFunc(d.models, func(model runtime.ModelChoice) bool {
    return strings.EqualFold(model.Ref, query) || strings.EqualFold(model.Provider+"/"+model.Model, query)
}) {
    d.filtered = []runtime.ModelChoice{{   // ← replaces, does not append
        Name: "Custom: " + query,
        Ref:  query,
    }}
}

The condition checks d.models (all models, for an exact match) — not d.filtered (fuzzy results already computed). So for a partial query like openai/gpt, modelpicker.Filter may have returned several relevant matches, but since "openai/gpt" is not an exact EqualFold match for any model's Ref or Provider+"/"+Model, those fuzzy results are discarded and only "Custom: openai/gpt" is shown.

Suggested fix: append the custom entry to the existing fuzzy results rather than replacing them, and only show the custom entry when fuzzy search returns nothing:

if isCustomQuery && !slices.ContainsFunc(d.models, func(model runtime.ModelChoice) bool {
    return strings.EqualFold(model.Ref, query) || strings.EqualFold(model.Provider+"/"+model.Model, query)
}) {
    customEntry := runtime.ModelChoice{
        Name: "Custom: " + query,
        Ref:  query,
    }
    if len(d.filtered) == 0 {
        d.filtered = []runtime.ModelChoice{customEntry}
    } else {
        d.filtered = append(d.filtered, customEntry)
    }
}

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.

Good catch. This was a real issue: partial provider/model queries could produce useful fuzzy matches, but the custom entry replaced them. The picker now appends the custom entry after the fuzzy results, and the test covers retaining a fuzzy match alongside the custom option. Fixed in fe27014.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the quick fix! The refactoring looks solid — extracting the fuzzy search into a shared package is a nice improvement, and appending the custom entry after fuzzy matches solves the replacement issue perfectly. The updated test also correctly validates that both fuzzy matches and the custom option are retained.

rumpl added 2 commits July 16, 2026 00:30
Extract model-choice filtering and ranking into a shared package backed by the existing fzf matcher. Search across the display name, provider, model ID, and reference while preserving the configured order for an empty query.

Update the full TUI model picker to use the shared search so other interfaces can provide identical matching and ranking without duplicating the implementation.

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
Add the /model command to the lean TUI with direct provider/model selection, default-model reset, and an interactive scoped completion list.

Use the shared fzf model scorer so searching by display name, provider, model ID, or reference behaves consistently with the full TUI. Restore regular slash-command completions when the model picker is dismissed.

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
@rumpl
rumpl force-pushed the feat-lean-model-picker branch from f0cb625 to 381267f Compare July 15, 2026 22:31
@aheritier aheritier added kind/feat PR adds a new feature (maps to feat:). Use on PRs only. area/tui For features/issues/fixes related to the TUI labels Jul 15, 2026
@rumpl
rumpl enabled auto-merge July 15, 2026 22:36
@rumpl
rumpl merged commit a095770 into docker:main Jul 16, 2026
10 checks passed
aheritier added a commit that referenced this pull request Jul 17, 2026
- Add Mermaid diagram section under Thinking and Tool Details
  documenting which diagram types are rendered inline and the fallback
  behavior for unsupported types (PR #3679)
- Update the Lean TUI section to list supported slash commands and
  explicitly note that /model is available (PR #3674)
- Update Runtime Model Switching to clarify that /model works in both
  the full TUI and the lean TUI, while Ctrl+M is full TUI only
social4hyq pushed a commit to social4hyq/homebrew-core that referenced this pull request Sep 20, 2026
docker-agent 1.111.0

Created-by: HarmonybrewBot
Commit-by: HarmonybrewBot
Merged-by: HarmonybrewBot
Description: Created by `brew bump`

---

Created with `brew bump-formula-pr`.<details>
  <summary>release notes</summary>
  <pre>This release adds a new scheduler toolset, inline Mermaid diagram rendering, model switching in the lean TUI, project config autodiscovery, and a range of bug fixes and performance improvements across the agent, TUI, and server.

## What's New

- Adds a built-in `scheduler` toolset with `create_schedule`, `list_schedules`, and `cancel_schedule` tools, enabling agents to schedule instructions to run once at a time, after a delay, or on a recurring interval
- Adds prompt cache miss warnings via an opt-in `warn_on_cache_miss` user setting that emits notifications when cached input tokens are absent after the first session response
- Adds inline Mermaid diagram rendering in the TUI
- Adds model switching (`/model`) to the lean TUI and shares fuzzy model search between the lean and full TUIs
- Adds autodiscovery of `docker-agent.yaml`, `docker-agent.yml`, and `docker-agent.hcl` project config files for no-argument local `docker agent run`
- Surfaces background-job elicitation (user-input) requests in the TUI, ensuring each request opens exactly one dialog regardless of which runtime delivers it
- Makes a custom `base_url` on a model automatically imply `bypass_models_gateway: true`, removing the need to set it explicitly

## Improvements

- Improves session cost details in the TUI: aligns layout, color-codes cost percentages per message, groups averages together, and puts "Total" on its own line
- Speeds up model switcher credential discovery by deduplicating credential names and resolving lookups concurrently, reducing model-picker latency
- Parallelizes toolset startup and prefetches the MCP catalog off the load critical path, reducing agent startup time

## Bug Fixes

- Fixes glob wildcard matching in permissions so `*` and `?` span path separators (`/`) in argument values such as file paths and URLs
- Fixes a bug where setting `defer_all: true` on a toolset caused the toolset's custom `Instructions` to be permanently dropped from the agent's context
- Fixes `SafetyPolicy` not persisting across turns, causing users to be re-prompted for tool approvals they had already opted into
- Fixes background elicitations over the API so requests from concurrent background jobs are replayable and answerable instead of being auto-declined
- Fixes Anthropic cache breakpoints exceeding the hard limit of 4 when deferred tools are present, which caused API errors at runtime
- Fixes the TUI editor not regaining focus after external editing (`Ctrl+G`), which caused Enter to route to the transcript instead of sending the composed message
- Fixes the scheduler schema so `type: scheduler` is accepted in agent config files (was previously rejected by JSON schema validation)
- Fixes foreground elicitations being delivered twice into the app event stream
- Fixes a mutex unlock in the scheduler's `setRuntime` method to use `defer` for safer lock release
- Fixes VCR cassette path normalization for portable prompt-file matching
- Fixes CI workflow YAML indentation that caused all CI runs to fail before any job could start

## Technical Changes

- Adds `MapSlice` fan-out helper in the concurrent utilities package and uses it for parallel toolset operations
- Serializes interactive OAuth flows across toolsets to prevent concurrent conflicts
- Brings the documentation portal to WCAG 2.1 AA conformance across light and dark themes and adds a pa11y-ci CI gate to maintain it
- Adds new documentation pages for the scheduler toolset, sandbox templates, custom commands, user settings, context and compaction guide, and fixes stale command syntax in the Named Commands docs
---

## What's Changed
* docs: update CHANGELOG.md for v1.110.0 by @docker-read-write[bot] in docker/docker-agent#3665
* fix: repair CI workflow YAML and failing AgentsMd e2e test on main by @dgageot in docker/docker-agent#3666
* feat(tools): add "scheduler" toolset to run instructions on a time or recurring interval by @dwin-gharibi in docker/docker-agent#3632
* fix(config): add scheduler toolset to agent-schema.json type enum by @aheritier in docker/docker-agent#3670
* feat: custom base_url implies bypass_models_gateway by @dgageot in docker/docker-agent#3667
* feat: add prompt cache miss warnings by @rumpl in docker/docker-agent#3671
* Improve session cost details by @rumpl in docker/docker-agent#3672
* perf: speed up model switcher credential discovery by @rumpl in docker/docker-agent#3673
* Add model switching to the lean TUI by @rumpl in docker/docker-agent#3674
* feat(tui,app): surface & correlate background-job elicitations (#3584) by @aheritier in docker/docker-agent#3624
* chore: bump go-isatty, openai-go, and libopenapi by @dgageot in docker/docker-agent#3675
* ci: bump golangci-lint-action to v9.3.0 and fix DeferMutexUnlock lint offense by @dgageot in docker/docker-agent#3676
* ci: bump pinned actions to latest same-major versions by @dgageot in docker/docker-agent#3677
* feat(server): session-scoped elicitation sink for API/server runtimes (#3584) by @aheritier in docker/docker-agent#3625
* Render Mermaid diagrams inline by @rumpl in docker/docker-agent#3679
* fix(server): surface background elicitations over API by @Sayt-0 in docker/docker-agent#3678
* fix(teamloader): preserve deferred toolset instructions by @Piyush0049 in docker/docker-agent#3680
* fix(permissions): match glob wildcards across path separators by @parveshsaini in docker/docker-agent#3605
* docs(agents): fix stale command syntax and sub_agents claim in Named Commands by @aheritier in docker/docker-agent#3684
* docs(sandbox): document Docker Sandboxes integration and published sbx templates by @aheritier in docker/docker-agent#3682
* docs: surface orphan pages and add compaction, commands, and user-settings docs by @aheritier in docker/docker-agent#3683
* fix(docs): WCAG 2.1 AA accessibility remediation for the docs portal by @aheritier in docker/docker-agent#3687
* fix(tui): refocus the editor after external editing so Enter sends the content by @dgageot in docker/docker-agent#3690
* Implements the path-aware, two-tier documentation accessibility CI gate by @aheritier in docker/docker-agent#3691
* feat(run): autodiscover project agent config by @HajimohammadiNet in docker/docker-agent#3681
* perf: parallelize toolset startup and prefetch MCP catalog by @dgageot in docker/docker-agent#3688
* fix(anthropic): keep cache breakpoints within Anthropic's limit of 4 with deferred tools by @dgageot in docker/docker-agent#3692
* fix(docs): render --link-hover on light-mode callout-link hover by @aheritier in docker/docker-agent#3693
* build(deps): bump the pip group across 1 directory with 2 updates by @dependabot[bot] in docker/docker-agent#3697
* fix(session): persist SafetyPolicy across turns by @trungutt in docker/docker-agent#3685

## New Contributors
* @parveshsaini made their first contribution in docker/docker-agent#3605
* @HajimohammadiNet made their first contribution in docker/docker-agent#3681

**Full Changelog**: docker/docker-agent@v1.110.0...v1.111.0
</pre>
  <p>View the full release notes at <a href="https://github.com/docker/docker-agent/releases/tag/v1.111.0">https://github.com/docker/docker-agent/releases/tag/v1.111.0</a>.</p>
</details>
<hr>

See merge request: Harmonybrew/homebrew-core!14465
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/tui For features/issues/fixes related to the TUI kind/feat PR adds a new feature (maps to feat:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants