Background revalidate provider models when model selector opens

On fresh startup the initial provider snapshot request can return
before providers finish loading, leaving the model picker empty.
The server broadcasts updates once providers are ready, but if the
user opens the picker in the interim they see "No models match".

Invalidate the React Query cache (background refetch, no loading
flash) every time the model selector popup opens so stale data is
silently refreshed.
This commit is contained in:
Mohamed Boudra
2026-04-10 11:01:01 +07:00
parent 32fe4b3beb
commit 1d41a50e1f
6 changed files with 34 additions and 2 deletions
@@ -88,6 +88,7 @@ type ControlledAgentStatusBarProps = {
features?: AgentFeature[];
onSetFeature?: (featureId: string, value: unknown) => void;
onDropdownClose?: () => void;
onModelSelectorOpen?: () => void;
};
export interface DraftAgentStatusBarProps {
@@ -110,6 +111,7 @@ export interface DraftAgentStatusBarProps {
features?: AgentFeature[];
onSetFeature?: (featureId: string, value: unknown) => void;
onDropdownClose?: () => void;
onModelSelectorOpen?: () => void;
disabled?: boolean;
}
@@ -217,6 +219,7 @@ function ControlledStatusBar({
features,
onSetFeature,
onDropdownClose,
onModelSelectorOpen,
}: ControlledAgentStatusBarProps) {
const { theme } = useUnistyles();
const isWeb = Platform.OS === "web";
@@ -411,6 +414,7 @@ function ControlledStatusBar({
onToggleFavorite={onToggleFavoriteModel}
isLoading={isModelLoading}
disabled={modelDisabled}
onOpen={onModelSelectorOpen}
onClose={onDropdownClose}
/>
</View>
@@ -662,6 +666,7 @@ function ControlledStatusBar({
onToggleFavorite={onToggleFavoriteModel}
isLoading={isModelLoading}
disabled={modelDisabled}
onOpen={onModelSelectorOpen}
onClose={onDropdownClose}
renderTrigger={({ selectedModelLabel }) => (
<View
@@ -875,6 +880,7 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
entries: snapshotEntries,
isLoading: snapshotIsLoading,
isFetching: snapshotIsFetching,
invalidate: invalidateSnapshot,
} = useProvidersSnapshot(serverId);
const snapshotModels = useMemo(() => {
@@ -1035,6 +1041,7 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
});
}}
isModelLoading={snapshotIsLoading || snapshotIsFetching}
onModelSelectorOpen={invalidateSnapshot}
onDropdownClose={onDropdownClose}
disabled={!client}
/>
@@ -1061,6 +1068,7 @@ export function DraftAgentStatusBar({
features,
onSetFeature,
onDropdownClose,
onModelSelectorOpen,
disabled = false,
}: DraftAgentStatusBarProps) {
const isWeb = Platform.OS === "web";
@@ -1105,6 +1113,7 @@ export function DraftAgentStatusBar({
}}
isLoading={isAllModelsLoading}
disabled={disabled}
onOpen={onModelSelectorOpen}
onClose={onDropdownClose}
/>
<ControlledStatusBar
@@ -1154,6 +1163,7 @@ export function DraftAgentStatusBar({
onSelectThinkingOption={onSelectThinkingOption}
features={features}
onSetFeature={onSetFeature}
onModelSelectorOpen={onModelSelectorOpen}
disabled={disabled}
/>
</>
@@ -59,6 +59,7 @@ interface CombinedModelSelectorProps {
disabled: boolean;
isOpen: boolean;
}) => React.ReactNode;
onOpen?: () => void;
onClose?: () => void;
disabled?: boolean;
}
@@ -517,6 +518,7 @@ export function CombinedModelSelector({
favoriteKeys = new Set<string>(),
onToggleFavorite,
renderTrigger,
onOpen,
onClose,
disabled = false,
}: CombinedModelSelectorProps) {
@@ -541,12 +543,14 @@ export function CombinedModelSelector({
(open: boolean) => {
setIsOpen(open);
setView(singleProviderView ?? { kind: "all" });
if (!open) {
if (open) {
onOpen?.();
} else {
setSearchQuery("");
onClose?.();
}
},
[onClose, singleProviderView],
[onOpen, onClose, singleProviderView],
);
const handleSelect = useCallback(
@@ -93,6 +93,7 @@ type UseAgentFormStateResult = {
isModelLoading: boolean;
modelError: string | null;
refreshProviderModels: () => void;
invalidateProviderModels: () => void;
setProviderAndModelFromUser: (provider: AgentProvider, modelId: string) => void;
workingDirIsEmpty: boolean;
persistFormPreferences: () => Promise<void>;
@@ -375,6 +376,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
isFetching: snapshotIsFetching,
error: snapshotError,
refresh: refreshSnapshot,
invalidate: invalidateSnapshot,
} = useProvidersSnapshot(formState.serverId);
const allProviderEntries = useMemo(() => snapshotEntries ?? [], [snapshotEntries]);
@@ -648,6 +650,10 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
refreshSnapshot();
}, [refreshSnapshot]);
const invalidateProviderModels = useCallback(() => {
invalidateSnapshot();
}, [invalidateSnapshot]);
const persistFormPreferences = useCallback(async () => {
const resolvedModel = resolveEffectiveModel(availableModels, formState.model);
const modelId = resolvedModel?.id ?? formState.model;
@@ -714,6 +720,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
isModelLoading,
modelError,
refreshProviderModels,
invalidateProviderModels,
setProviderAndModelFromUser,
workingDirIsEmpty,
persistFormPreferences,
@@ -745,6 +752,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
isModelLoading,
modelError,
refreshProviderModels,
invalidateProviderModels,
setProviderAndModelFromUser,
workingDirIsEmpty,
persistFormPreferences,
@@ -17,6 +17,7 @@ interface UseProvidersSnapshotResult {
error: string | null;
supportsSnapshot: boolean;
refresh: () => void;
invalidate: () => void;
}
export function useProvidersSnapshot(serverId: string | null): UseProvidersSnapshotResult {
@@ -66,6 +67,10 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
void client.refreshProvidersSnapshot();
}, [client]);
const invalidate = useCallback(() => {
void queryClient.invalidateQueries({ queryKey });
}, [queryClient, queryKey]);
return {
entries: snapshotQuery.data?.entries ?? undefined,
isLoading: snapshotQuery.isLoading,
@@ -73,6 +78,7 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
error: snapshotQuery.error instanceof Error ? snapshotQuery.error.message : null,
supportsSnapshot,
refresh,
invalidate,
};
}
@@ -226,6 +226,7 @@ function DraftAgentScreenContent({
isModelLoading,
modelError,
refreshProviderModels,
invalidateProviderModels,
setProviderAndModelFromUser,
persistFormPreferences,
} = useAgentFormState({
@@ -1268,6 +1269,7 @@ function DraftAgentScreenContent({
onSelectThinkingOption: setThinkingOptionFromUser,
features: draftFeatures,
onSetFeature: setDraftFeatureValue,
onModelSelectorOpen: invalidateProviderModels,
disabled: isSubmitting,
}}
/>
@@ -80,6 +80,7 @@ export function WorkspaceDraftAgentTab({
availableThinkingOptions,
isModelLoading,
setProviderAndModelFromUser,
invalidateProviderModels,
persistFormPreferences,
} = useAgentFormState({
initialServerId: serverId,
@@ -373,6 +374,7 @@ export function WorkspaceDraftAgentTab({
features: draftFeatures,
onSetFeature: handleSetFeatureWithFocus,
onDropdownClose: () => focusInputRef.current?.(),
onModelSelectorOpen: invalidateProviderModels,
disabled: isSubmitting,
}}
/>