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:
@@ -88,6 +88,7 @@ type ControlledAgentStatusBarProps = {
|
|||||||
features?: AgentFeature[];
|
features?: AgentFeature[];
|
||||||
onSetFeature?: (featureId: string, value: unknown) => void;
|
onSetFeature?: (featureId: string, value: unknown) => void;
|
||||||
onDropdownClose?: () => void;
|
onDropdownClose?: () => void;
|
||||||
|
onModelSelectorOpen?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface DraftAgentStatusBarProps {
|
export interface DraftAgentStatusBarProps {
|
||||||
@@ -110,6 +111,7 @@ export interface DraftAgentStatusBarProps {
|
|||||||
features?: AgentFeature[];
|
features?: AgentFeature[];
|
||||||
onSetFeature?: (featureId: string, value: unknown) => void;
|
onSetFeature?: (featureId: string, value: unknown) => void;
|
||||||
onDropdownClose?: () => void;
|
onDropdownClose?: () => void;
|
||||||
|
onModelSelectorOpen?: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +219,7 @@ function ControlledStatusBar({
|
|||||||
features,
|
features,
|
||||||
onSetFeature,
|
onSetFeature,
|
||||||
onDropdownClose,
|
onDropdownClose,
|
||||||
|
onModelSelectorOpen,
|
||||||
}: ControlledAgentStatusBarProps) {
|
}: ControlledAgentStatusBarProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const isWeb = Platform.OS === "web";
|
const isWeb = Platform.OS === "web";
|
||||||
@@ -411,6 +414,7 @@ function ControlledStatusBar({
|
|||||||
onToggleFavorite={onToggleFavoriteModel}
|
onToggleFavorite={onToggleFavoriteModel}
|
||||||
isLoading={isModelLoading}
|
isLoading={isModelLoading}
|
||||||
disabled={modelDisabled}
|
disabled={modelDisabled}
|
||||||
|
onOpen={onModelSelectorOpen}
|
||||||
onClose={onDropdownClose}
|
onClose={onDropdownClose}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -662,6 +666,7 @@ function ControlledStatusBar({
|
|||||||
onToggleFavorite={onToggleFavoriteModel}
|
onToggleFavorite={onToggleFavoriteModel}
|
||||||
isLoading={isModelLoading}
|
isLoading={isModelLoading}
|
||||||
disabled={modelDisabled}
|
disabled={modelDisabled}
|
||||||
|
onOpen={onModelSelectorOpen}
|
||||||
onClose={onDropdownClose}
|
onClose={onDropdownClose}
|
||||||
renderTrigger={({ selectedModelLabel }) => (
|
renderTrigger={({ selectedModelLabel }) => (
|
||||||
<View
|
<View
|
||||||
@@ -875,6 +880,7 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
|
|||||||
entries: snapshotEntries,
|
entries: snapshotEntries,
|
||||||
isLoading: snapshotIsLoading,
|
isLoading: snapshotIsLoading,
|
||||||
isFetching: snapshotIsFetching,
|
isFetching: snapshotIsFetching,
|
||||||
|
invalidate: invalidateSnapshot,
|
||||||
} = useProvidersSnapshot(serverId);
|
} = useProvidersSnapshot(serverId);
|
||||||
|
|
||||||
const snapshotModels = useMemo(() => {
|
const snapshotModels = useMemo(() => {
|
||||||
@@ -1035,6 +1041,7 @@ export function AgentStatusBar({ agentId, serverId, onDropdownClose }: AgentStat
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
isModelLoading={snapshotIsLoading || snapshotIsFetching}
|
isModelLoading={snapshotIsLoading || snapshotIsFetching}
|
||||||
|
onModelSelectorOpen={invalidateSnapshot}
|
||||||
onDropdownClose={onDropdownClose}
|
onDropdownClose={onDropdownClose}
|
||||||
disabled={!client}
|
disabled={!client}
|
||||||
/>
|
/>
|
||||||
@@ -1061,6 +1068,7 @@ export function DraftAgentStatusBar({
|
|||||||
features,
|
features,
|
||||||
onSetFeature,
|
onSetFeature,
|
||||||
onDropdownClose,
|
onDropdownClose,
|
||||||
|
onModelSelectorOpen,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: DraftAgentStatusBarProps) {
|
}: DraftAgentStatusBarProps) {
|
||||||
const isWeb = Platform.OS === "web";
|
const isWeb = Platform.OS === "web";
|
||||||
@@ -1105,6 +1113,7 @@ export function DraftAgentStatusBar({
|
|||||||
}}
|
}}
|
||||||
isLoading={isAllModelsLoading}
|
isLoading={isAllModelsLoading}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
onOpen={onModelSelectorOpen}
|
||||||
onClose={onDropdownClose}
|
onClose={onDropdownClose}
|
||||||
/>
|
/>
|
||||||
<ControlledStatusBar
|
<ControlledStatusBar
|
||||||
@@ -1154,6 +1163,7 @@ export function DraftAgentStatusBar({
|
|||||||
onSelectThinkingOption={onSelectThinkingOption}
|
onSelectThinkingOption={onSelectThinkingOption}
|
||||||
features={features}
|
features={features}
|
||||||
onSetFeature={onSetFeature}
|
onSetFeature={onSetFeature}
|
||||||
|
onModelSelectorOpen={onModelSelectorOpen}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ interface CombinedModelSelectorProps {
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
|
onOpen?: () => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
@@ -517,6 +518,7 @@ export function CombinedModelSelector({
|
|||||||
favoriteKeys = new Set<string>(),
|
favoriteKeys = new Set<string>(),
|
||||||
onToggleFavorite,
|
onToggleFavorite,
|
||||||
renderTrigger,
|
renderTrigger,
|
||||||
|
onOpen,
|
||||||
onClose,
|
onClose,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: CombinedModelSelectorProps) {
|
}: CombinedModelSelectorProps) {
|
||||||
@@ -541,12 +543,14 @@ export function CombinedModelSelector({
|
|||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
setIsOpen(open);
|
setIsOpen(open);
|
||||||
setView(singleProviderView ?? { kind: "all" });
|
setView(singleProviderView ?? { kind: "all" });
|
||||||
if (!open) {
|
if (open) {
|
||||||
|
onOpen?.();
|
||||||
|
} else {
|
||||||
setSearchQuery("");
|
setSearchQuery("");
|
||||||
onClose?.();
|
onClose?.();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onClose, singleProviderView],
|
[onOpen, onClose, singleProviderView],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ type UseAgentFormStateResult = {
|
|||||||
isModelLoading: boolean;
|
isModelLoading: boolean;
|
||||||
modelError: string | null;
|
modelError: string | null;
|
||||||
refreshProviderModels: () => void;
|
refreshProviderModels: () => void;
|
||||||
|
invalidateProviderModels: () => void;
|
||||||
setProviderAndModelFromUser: (provider: AgentProvider, modelId: string) => void;
|
setProviderAndModelFromUser: (provider: AgentProvider, modelId: string) => void;
|
||||||
workingDirIsEmpty: boolean;
|
workingDirIsEmpty: boolean;
|
||||||
persistFormPreferences: () => Promise<void>;
|
persistFormPreferences: () => Promise<void>;
|
||||||
@@ -375,6 +376,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
|
|||||||
isFetching: snapshotIsFetching,
|
isFetching: snapshotIsFetching,
|
||||||
error: snapshotError,
|
error: snapshotError,
|
||||||
refresh: refreshSnapshot,
|
refresh: refreshSnapshot,
|
||||||
|
invalidate: invalidateSnapshot,
|
||||||
} = useProvidersSnapshot(formState.serverId);
|
} = useProvidersSnapshot(formState.serverId);
|
||||||
|
|
||||||
const allProviderEntries = useMemo(() => snapshotEntries ?? [], [snapshotEntries]);
|
const allProviderEntries = useMemo(() => snapshotEntries ?? [], [snapshotEntries]);
|
||||||
@@ -648,6 +650,10 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
|
|||||||
refreshSnapshot();
|
refreshSnapshot();
|
||||||
}, [refreshSnapshot]);
|
}, [refreshSnapshot]);
|
||||||
|
|
||||||
|
const invalidateProviderModels = useCallback(() => {
|
||||||
|
invalidateSnapshot();
|
||||||
|
}, [invalidateSnapshot]);
|
||||||
|
|
||||||
const persistFormPreferences = useCallback(async () => {
|
const persistFormPreferences = useCallback(async () => {
|
||||||
const resolvedModel = resolveEffectiveModel(availableModels, formState.model);
|
const resolvedModel = resolveEffectiveModel(availableModels, formState.model);
|
||||||
const modelId = resolvedModel?.id ?? formState.model;
|
const modelId = resolvedModel?.id ?? formState.model;
|
||||||
@@ -714,6 +720,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
|
|||||||
isModelLoading,
|
isModelLoading,
|
||||||
modelError,
|
modelError,
|
||||||
refreshProviderModels,
|
refreshProviderModels,
|
||||||
|
invalidateProviderModels,
|
||||||
setProviderAndModelFromUser,
|
setProviderAndModelFromUser,
|
||||||
workingDirIsEmpty,
|
workingDirIsEmpty,
|
||||||
persistFormPreferences,
|
persistFormPreferences,
|
||||||
@@ -745,6 +752,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
|
|||||||
isModelLoading,
|
isModelLoading,
|
||||||
modelError,
|
modelError,
|
||||||
refreshProviderModels,
|
refreshProviderModels,
|
||||||
|
invalidateProviderModels,
|
||||||
setProviderAndModelFromUser,
|
setProviderAndModelFromUser,
|
||||||
workingDirIsEmpty,
|
workingDirIsEmpty,
|
||||||
persistFormPreferences,
|
persistFormPreferences,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface UseProvidersSnapshotResult {
|
|||||||
error: string | null;
|
error: string | null;
|
||||||
supportsSnapshot: boolean;
|
supportsSnapshot: boolean;
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
|
invalidate: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useProvidersSnapshot(serverId: string | null): UseProvidersSnapshotResult {
|
export function useProvidersSnapshot(serverId: string | null): UseProvidersSnapshotResult {
|
||||||
@@ -66,6 +67,10 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
|
|||||||
void client.refreshProvidersSnapshot();
|
void client.refreshProvidersSnapshot();
|
||||||
}, [client]);
|
}, [client]);
|
||||||
|
|
||||||
|
const invalidate = useCallback(() => {
|
||||||
|
void queryClient.invalidateQueries({ queryKey });
|
||||||
|
}, [queryClient, queryKey]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entries: snapshotQuery.data?.entries ?? undefined,
|
entries: snapshotQuery.data?.entries ?? undefined,
|
||||||
isLoading: snapshotQuery.isLoading,
|
isLoading: snapshotQuery.isLoading,
|
||||||
@@ -73,6 +78,7 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
|
|||||||
error: snapshotQuery.error instanceof Error ? snapshotQuery.error.message : null,
|
error: snapshotQuery.error instanceof Error ? snapshotQuery.error.message : null,
|
||||||
supportsSnapshot,
|
supportsSnapshot,
|
||||||
refresh,
|
refresh,
|
||||||
|
invalidate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ function DraftAgentScreenContent({
|
|||||||
isModelLoading,
|
isModelLoading,
|
||||||
modelError,
|
modelError,
|
||||||
refreshProviderModels,
|
refreshProviderModels,
|
||||||
|
invalidateProviderModels,
|
||||||
setProviderAndModelFromUser,
|
setProviderAndModelFromUser,
|
||||||
persistFormPreferences,
|
persistFormPreferences,
|
||||||
} = useAgentFormState({
|
} = useAgentFormState({
|
||||||
@@ -1268,6 +1269,7 @@ function DraftAgentScreenContent({
|
|||||||
onSelectThinkingOption: setThinkingOptionFromUser,
|
onSelectThinkingOption: setThinkingOptionFromUser,
|
||||||
features: draftFeatures,
|
features: draftFeatures,
|
||||||
onSetFeature: setDraftFeatureValue,
|
onSetFeature: setDraftFeatureValue,
|
||||||
|
onModelSelectorOpen: invalidateProviderModels,
|
||||||
disabled: isSubmitting,
|
disabled: isSubmitting,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export function WorkspaceDraftAgentTab({
|
|||||||
availableThinkingOptions,
|
availableThinkingOptions,
|
||||||
isModelLoading,
|
isModelLoading,
|
||||||
setProviderAndModelFromUser,
|
setProviderAndModelFromUser,
|
||||||
|
invalidateProviderModels,
|
||||||
persistFormPreferences,
|
persistFormPreferences,
|
||||||
} = useAgentFormState({
|
} = useAgentFormState({
|
||||||
initialServerId: serverId,
|
initialServerId: serverId,
|
||||||
@@ -373,6 +374,7 @@ export function WorkspaceDraftAgentTab({
|
|||||||
features: draftFeatures,
|
features: draftFeatures,
|
||||||
onSetFeature: handleSetFeatureWithFocus,
|
onSetFeature: handleSetFeatureWithFocus,
|
||||||
onDropdownClose: () => focusInputRef.current?.(),
|
onDropdownClose: () => focusInputRef.current?.(),
|
||||||
|
onModelSelectorOpen: invalidateProviderModels,
|
||||||
disabled: isSubmitting,
|
disabled: isSubmitting,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user