add provider-specific model picker with mid-chat switching#91
add provider-specific model picker with mid-chat switching#91tulsi-builder wants to merge 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8e3640391
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Some(stored_provider) = mapping["provider_id"].as_str() { | ||
| if stored_provider != expected_provider.as_str() { | ||
| return None; | ||
| } | ||
| } |
There was a problem hiding this comment.
Reject legacy session mappings without provider IDs
The new provider guard only rejects cached ACP session IDs when provider_id exists and differs, but pre-upgrade mapping files do not contain provider_id, so they are still reused across providers. In that upgrade path, switching providers can still load an old session ID from another provider and re-trigger the same load_session/"Resource not found" failures this change is meant to eliminate. Treat missing provider_id as incompatible when expected_provider is set.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
fixed. Legacy files without provider_id are now rejected when we expect a provider
| currentModelId: modelState.currentModelId, | ||
| currentModelName: | ||
| modelState.currentModelName ?? modelState.currentModelId, |
There was a problem hiding this comment.
Preserve home model choice during model-state refresh
This write always replaces the provider's current model with ACP's reported default, and the effect reruns on selectedProject/persona/provider changes. If a user picks a non-default model on Home and then changes project (or another dependency triggers bootstrap), their selection is silently overwritten before sending. This makes the picker state unstable and can cause the first chat to start with the wrong model.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Valid edge case. Unlikely to hit in normal usage (requires model pick --> project/persona change before sending), and the upcoming backend rewrite will change this bootstrap flow. Leaving as-is for now.
…tching Backend: - Expand acp:model_state payload to include availableModels from both SessionModelState and SessionConfigOption paths. - Add acp_get_model_state and acp_set_model Tauri commands. - Provider-scoped session storage prevents cross-provider session reuse. - load_session falls back to new_session when stored session ID is stale. - Bootstrap no longer persists agent session IDs (only driver.run does). Frontend: - Wire ChatView -> ChatInput -> ChatInputToolbar with real model data from shared modelStateByProvider store. - HomeScreen writes to shared store so ChatView reads cached models instantly. - ChatView skips ACP bootstrap when cached models exist for the provider. - setModelState preserves explicit user model selection over provider defaults. - Model and provider switches show system notifications in chat timeline. - Pre-send model sync in useChat applies selected model (best-effort). - Fix modelsLoading stuck true when bootstrap effects were cancelled. Tests: - Model picker rendering, loading state, and selection callback coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f8e3640 to
a05aa46
Compare
Overview
Category: new-feature
User Impact: Users can now see and select which AI model their provider uses, switch models or providers mid-conversation, and see a notification when the switch happens.
Problem: The model picker dropdown existed in the UI but was completely unpowered — it showed no models, couldn't be clicked, and changing providers mid-chat caused "Resource not found" errors from stale ACP session IDs.
Solution: Wire the existing dropdown to real model data from ACP providers (supporting both
SessionModelStateandSessionConfigOptionpaths), cache model lists in a shared store so navigation between Home and Chat is instant, and scope ACP session storage by provider to prevent cross-provider session reuse.Known limitations
set_modelcan't reliably apply to an existing session. The user's model selection is stored and the pre-send sync attempts to apply it, but the provider may use its default. Cross-provider switching (Goose → Codex → Claude Code) works reliably.acp_get_model_statecall. A potential fix is querying model state from the long-livedAcpSessionRegistrysession instead of spawning throwaway processes — flagged for engineering input.modelStateByProvideris in-memory only. Each provider's models are re-fetched on first visit to the Home screen.File changes
src-tauri/src/services/acp/payloads.rs
Added
AvailableModelPayloadand expandedModelStatePayloadto includesource,configId, andavailableModelsfields.src-tauri/src/services/acp/writer.rs
Emit model state from both
on_model_state_update(SessionModelState) andon_config_option_update(SessionConfigOption with model category). Added normalization helpers for both paths.src-tauri/src/commands/acp.rs
Added
acp_get_model_stateandacp_set_modelTauri commands.acp_get_model_statenormalizes model state from either ACP path and falls back fromload_sessiontonew_sessionwhen stored session IDs are stale. Bootstrap no longer persists agent session IDs to avoid poisoning the send path.src-tauri/src/lib.rs
Registered new
acp_get_model_stateandacp_set_modelcommands.src-tauri/src/services/acp/store.rs
TauriStorenow tracksprovider_id. Cached agent session IDs are only returned when the stored provider matches, preventing cross-provider session reuse.src-tauri/src/services/acp/mod.rs
Send path now passes provider ID to
TauriStore::with_providerso session lookups are provider-scoped.src/shared/types/chat.ts
Added
ModelOption,ModelSelectionSource, andProviderModelStatetypes.src/shared/api/acp.ts
Added
acpGetModelStateandacpSetModelfrontend API wrappers.src/features/chat/stores/chatSessionStore.ts
Added
modelStateByProviderto store andsetModelStateaction.setModelStatepreserves explicit user model selections when the provider reports a different default.src/features/chat/hooks/useAcpStream.ts
Captures
acp:model_stateevents and writes to the shared store.src/features/chat/hooks/useChat.ts
Pre-send model sync attempts to apply the user's selected model before sending. Wrapped in try/catch so failures don't block the message.
src/features/chat/ui/ChatView.tsx
Bootstrap effect waits for
effectiveWorkingDir, uses cached models from shared store when available, and avoids cascading re-fires. Model and provider switches inject system notifications.currentModelderivation simplified to a clearuseMemowith explicit priority.src/features/chat/ui/ChatInput.tsx
Accepts and passes through model picker props (
currentModel,currentModelId,modelsLoading,availableModels,onModelChange).src/features/chat/ui/ChatInputToolbar.tsx
Model dropdown renders from
availableModels, shows loading state, and callsonModelChangeon selection.src/features/home/ui/HomeScreen.tsx
Model state reads/writes from shared
modelStateByProviderstore instead of local useState. Bootstrap writes to store so ChatView reads cached data on navigation.src/app/AppShell.tsx
Passes model ID and name from Home screen into new chat session creation.
src/app/hooks/useAppStartup.ts
No functional change (pre-warm was added then reverted).
scripts/check-file-sizes.mjs
Added size exceptions for ChatView.tsx and acp.rs with justification.
src/features/chat/ui/tests/ChatInput.test.tsx
Added tests for model picker rendering, loading state, and selection callback.
src/features/home/ui/HomeScreen.test.tsx
Updated mock shapes for model bootstrap.
Reproduction Steps