Fix provider health banner showing during onboarding (#372)

* disable provider health query until edited is true, set initial provider health cache to true when onboarding mutation succeeds

* fix scroll issue with model selector in dialog
This commit is contained in:
Cole Goldsmith 2025-11-07 11:49:57 -06:00 committed by GitHub
parent 2aecfc570c
commit 7e343ed1f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 37 additions and 11 deletions

View file

@ -7,6 +7,7 @@ import { useProviderHealthQuery } from "@/src/app/api/queries/useProviderHealthQ
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { ModelProvider } from "@/app/settings/helpers/model-helpers";
interface ProviderHealthBannerProps { interface ProviderHealthBannerProps {
className?: string; className?: string;
@ -42,12 +43,22 @@ export function useProviderHealth() {
}; };
} }
const providerTitleMap: Record<ModelProvider, string> = {
openai: "OpenAI",
ollama: "Ollama",
watsonx: "IBM watsonx.ai",
};
export function ProviderHealthBanner({ className }: ProviderHealthBannerProps) { export function ProviderHealthBanner({ className }: ProviderHealthBannerProps) {
const { isLoading, isHealthy, isUnhealthy, health } = useProviderHealth(); const { isLoading, isHealthy, isUnhealthy, health } = useProviderHealth();
const router = useRouter(); const router = useRouter();
const { data: settings = {} } = useGetSettingsQuery(); const { data: settings = {} } = useGetSettingsQuery();
const providerTitle =
providerTitleMap[settings.provider?.model_provider as ModelProvider] ||
"Provider";
// Only show banner when provider is unhealthy (not when backend is unavailable) // Only show banner when provider is unhealthy (not when backend is unavailable)
if (isLoading || isHealthy) { if (isLoading || isHealthy) {
return null; return null;
@ -71,7 +82,7 @@ export function ProviderHealthBanner({ className }: ProviderHealthBannerProps) {
icon={AlertTriangle} icon={AlertTriangle}
/> />
<BannerTitle className="font-medium flex items-center gap-2"> <BannerTitle className="font-medium flex items-center gap-2">
{errorMessage} {providerTitle} error - {errorMessage}
</BannerTitle> </BannerTitle>
<Button size="sm" onClick={() => router.push(settingsUrl)}> <Button size="sm" onClick={() => router.push(settingsUrl)}>
Fix Setup Fix Setup

View file

@ -4,6 +4,7 @@ import {
useQueryClient, useQueryClient,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import type { Settings } from "../queries/useGetSettingsQuery"; import type { Settings } from "../queries/useGetSettingsQuery";
import { useGetCurrentProviderModelsQuery } from "../queries/useGetModelsQuery";
export interface UpdateSettingsRequest { export interface UpdateSettingsRequest {
// Agent settings // Agent settings
@ -37,6 +38,7 @@ export const useUpdateSettingsMutation = (
> >
) => { ) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { refetch: refetchModels } = useGetCurrentProviderModelsQuery();
async function updateSettings( async function updateSettings(
variables: UpdateSettingsRequest variables: UpdateSettingsRequest
@ -63,6 +65,7 @@ export const useUpdateSettingsMutation = (
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["settings"], queryKey: ["settings"],
}); });
refetchModels(); // Refetch models for the settings page
options?.onSuccess?.(...args); options?.onSuccess?.(...args);
}, },
onError: options?.onError, onError: options?.onError,

View file

@ -1,9 +1,9 @@
import { ModelProvider } from "@/app/settings/helpers/model-helpers";
import { import {
type UseQueryOptions, type UseQueryOptions,
useQuery, useQuery,
useQueryClient, useQueryClient,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import { useGetSettingsQuery } from "./useGetSettingsQuery";
export interface ProviderHealthDetails { export interface ProviderHealthDetails {
llm_model: string; llm_model: string;
@ -22,12 +22,6 @@ export interface ProviderHealthParams {
provider?: "openai" | "ollama" | "watsonx"; provider?: "openai" | "ollama" | "watsonx";
} }
const providerTitleMap: Record<ModelProvider, string> = {
openai: "OpenAI",
ollama: "Ollama",
watsonx: "IBM watsonx.ai",
};
export const useProviderHealthQuery = ( export const useProviderHealthQuery = (
params?: ProviderHealthParams, params?: ProviderHealthParams,
options?: Omit< options?: Omit<
@ -37,6 +31,8 @@ export const useProviderHealthQuery = (
) => { ) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data: settings = {} } = useGetSettingsQuery();
async function checkProviderHealth(): Promise<ProviderHealthResponse> { async function checkProviderHealth(): Promise<ProviderHealthResponse> {
try { try {
const url = new URL("/api/provider/health", window.location.origin); const url = new URL("/api/provider/health", window.location.origin);
@ -84,6 +80,7 @@ export const useProviderHealthQuery = (
queryKey: ["provider", "health"], queryKey: ["provider", "health"],
queryFn: checkProviderHealth, queryFn: checkProviderHealth,
retry: false, // Don't retry health checks automatically retry: false, // Don't retry health checks automatically
enabled: !!settings?.edited && options?.enabled !== false, // Only run after onboarding is complete
...options, ...options,
}, },
queryClient queryClient

View file

@ -57,7 +57,7 @@ export function ModelSelector({
}, [options, value, custom, onValueChange]); }, [options, value, custom, onValueChange]);
return ( return (
<Popover open={open} onOpenChange={setOpen}> <Popover open={open} onOpenChange={setOpen} modal={false}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
{/** biome-ignore lint/a11y/useSemanticElements: has to be a Button */} {/** biome-ignore lint/a11y/useSemanticElements: has to be a Button */}
<Button <Button
@ -99,7 +99,8 @@ export function ModelSelector({
</PopoverTrigger> </PopoverTrigger>
<PopoverContent <PopoverContent
align="start" align="start"
className=" p-0 w-[var(--radix-popover-trigger-width)]" className="p-0 w-[var(--radix-popover-trigger-width)]"
onOpenAutoFocus={(e) => e.preventDefault()}
> >
<Command> <Command>
<CommandInput <CommandInput
@ -107,7 +108,10 @@ export function ModelSelector({
value={searchValue} value={searchValue}
onValueChange={setSearchValue} onValueChange={setSearchValue}
/> />
<CommandList> <CommandList
className="max-h-[300px] overflow-y-auto"
onWheel={(e) => e.stopPropagation()}
>
<CommandEmpty>{noOptionsPlaceholder}</CommandEmpty> <CommandEmpty>{noOptionsPlaceholder}</CommandEmpty>
<CommandGroup> <CommandGroup>
{options.map((option) => ( {options.map((option) => (

View file

@ -26,6 +26,8 @@ import { IBMOnboarding } from "./ibm-onboarding";
import { OllamaOnboarding } from "./ollama-onboarding"; import { OllamaOnboarding } from "./ollama-onboarding";
import { OpenAIOnboarding } from "./openai-onboarding"; import { OpenAIOnboarding } from "./openai-onboarding";
import { TabTrigger } from "./tab-trigger"; import { TabTrigger } from "./tab-trigger";
import { ProviderHealthResponse } from "@/app/api/queries/useProviderHealthQuery";
import { useQueryClient } from "@tanstack/react-query";
interface OnboardingCardProps { interface OnboardingCardProps {
onComplete: () => void; onComplete: () => void;
@ -57,6 +59,8 @@ const OnboardingCard = ({
const [loadingStep, setLoadingStep] = useState<number>(0); const [loadingStep, setLoadingStep] = useState<number>(0);
const queryClient = useQueryClient();
// Reset loading step when models start loading // Reset loading step when models start loading
useEffect(() => { useEffect(() => {
if (isLoadingModels) { if (isLoadingModels) {
@ -129,6 +133,13 @@ const OnboardingCard = ({
const onboardingMutation = useOnboardingMutation({ const onboardingMutation = useOnboardingMutation({
onSuccess: (data) => { onSuccess: (data) => {
console.log("Onboarding completed successfully", data); console.log("Onboarding completed successfully", data);
// Update provider health cache to healthy since backend just validated
const healthData: ProviderHealthResponse = {
status: "healthy",
message: "Provider is configured and working correctly",
provider: settings.model_provider,
};
queryClient.setQueryData(["provider", "health"], healthData);
setCurrentStep(0); setCurrentStep(0);
setError(null); setError(null);
}, },