fix: error scenario when checking provider (#8394)
This commit is contained in:
@@ -33,7 +33,7 @@ interface ConfigContextType {
|
|||||||
extensionsList: FixedExtensionEntry[];
|
extensionsList: FixedExtensionEntry[];
|
||||||
extensionWarnings: string[];
|
extensionWarnings: string[];
|
||||||
upsert: (key: string, value: unknown, is_secret: boolean) => Promise<void>;
|
upsert: (key: string, value: unknown, is_secret: boolean) => Promise<void>;
|
||||||
read: (key: string, is_secret: boolean) => Promise<unknown>;
|
read: (key: string, is_secret: boolean, options?: { throwOnError?: boolean }) => Promise<unknown>;
|
||||||
remove: (key: string, is_secret: boolean) => Promise<void>;
|
remove: (key: string, is_secret: boolean) => Promise<void>;
|
||||||
addExtension: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>;
|
addExtension: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>;
|
||||||
toggleExtension: (name: string) => Promise<void>;
|
toggleExtension: (name: string) => Promise<void>;
|
||||||
@@ -88,11 +88,14 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
|
|||||||
[reloadConfig]
|
[reloadConfig]
|
||||||
);
|
);
|
||||||
|
|
||||||
const read = useCallback(async (key: string, is_secret: boolean = false) => {
|
const read = useCallback(async (key: string, is_secret: boolean = false, options?: { throwOnError?: boolean }) => {
|
||||||
const query: ConfigKeyQuery = { key: key, is_secret: is_secret };
|
const query: ConfigKeyQuery = { key: key, is_secret: is_secret };
|
||||||
const response = await readConfig({
|
const response = await readConfig({
|
||||||
body: query,
|
body: query,
|
||||||
});
|
});
|
||||||
|
if (options?.throwOnError && response.error) {
|
||||||
|
throw response.error;
|
||||||
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { useConfig } from '../ConfigContext';
|
import { useConfig } from '../ConfigContext';
|
||||||
import { useModelAndProvider } from '../ModelAndProviderContext';
|
import { useModelAndProvider } from '../ModelAndProviderContext';
|
||||||
import { Goose } from '../icons';
|
import { Goose } from '../icons';
|
||||||
|
import { Button } from '../ui/button';
|
||||||
import ProviderSelector from './ProviderSelector';
|
import ProviderSelector from './ProviderSelector';
|
||||||
import OnboardingSuccess from './OnboardingSuccess';
|
import OnboardingSuccess from './OnboardingSuccess';
|
||||||
import {
|
import {
|
||||||
@@ -23,6 +24,18 @@ const i18n = defineMessages({
|
|||||||
id: 'onboardingGuard.welcomeDescription',
|
id: 'onboardingGuard.welcomeDescription',
|
||||||
defaultMessage: 'Your local AI agent. Connect an AI model provider to get started.',
|
defaultMessage: 'Your local AI agent. Connect an AI model provider to get started.',
|
||||||
},
|
},
|
||||||
|
checkProviderErrorTitle: {
|
||||||
|
id: 'onboardingGuard.checkProviderErrorTitle',
|
||||||
|
defaultMessage: 'Unable to connect to Goose server',
|
||||||
|
},
|
||||||
|
checkProviderErrorDescription: {
|
||||||
|
id: 'onboardingGuard.checkProviderErrorDescription',
|
||||||
|
defaultMessage: 'The server may be starting up or temporarily unavailable.',
|
||||||
|
},
|
||||||
|
retry: {
|
||||||
|
id: 'onboardingGuard.retry',
|
||||||
|
defaultMessage: 'Retry',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED';
|
const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED';
|
||||||
@@ -39,6 +52,7 @@ export default function OnboardingGuard({ children }: OnboardingGuardProps) {
|
|||||||
|
|
||||||
const [isCheckingProvider, setIsCheckingProvider] = useState(true);
|
const [isCheckingProvider, setIsCheckingProvider] = useState(true);
|
||||||
const [hasProvider, setHasProvider] = useState(false);
|
const [hasProvider, setHasProvider] = useState(false);
|
||||||
|
const [checkProviderError, setCheckProviderError] = useState(false);
|
||||||
const [hasSelection, setHasSelection] = useState(false);
|
const [hasSelection, setHasSelection] = useState(false);
|
||||||
const [configuredProvider, setConfiguredProvider] = useState<string | null>(null);
|
const [configuredProvider, setConfiguredProvider] = useState<string | null>(null);
|
||||||
const [configuredProviderDisplayName, setConfiguredProviderDisplayName] = useState<string | null>(
|
const [configuredProviderDisplayName, setConfiguredProviderDisplayName] = useState<string | null>(
|
||||||
@@ -47,27 +61,37 @@ export default function OnboardingGuard({ children }: OnboardingGuardProps) {
|
|||||||
const [configuredModel, setConfiguredModel] = useState<string | null>(null);
|
const [configuredModel, setConfiguredModel] = useState<string | null>(null);
|
||||||
const hasTrackedOnboardingStart = useRef(false);
|
const hasTrackedOnboardingStart = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const checkProvider = async (retries = 3, delay = 1000) => {
|
||||||
const checkProvider = async () => {
|
setIsCheckingProvider(true);
|
||||||
|
setCheckProviderError(false);
|
||||||
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
||||||
try {
|
try {
|
||||||
const provider = ((await read('GOOSE_PROVIDER', false)) as string) || '';
|
const provider = (await read('GOOSE_PROVIDER', false, { throwOnError: true })) as string | null;
|
||||||
setHasProvider(provider.trim() !== '');
|
setHasProvider(!!provider?.trim());
|
||||||
} catch (error) {
|
|
||||||
console.error('Error checking provider:', error);
|
|
||||||
setHasProvider(false);
|
|
||||||
} finally {
|
|
||||||
setIsCheckingProvider(false);
|
setIsCheckingProvider(false);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error checking provider (attempt ${attempt + 1}/${retries + 1}):`, error);
|
||||||
|
if (attempt < retries) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
checkProvider();
|
setCheckProviderError(true);
|
||||||
}, [read]);
|
setIsCheckingProvider(false);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isCheckingProvider && !hasProvider && !hasTrackedOnboardingStart.current) {
|
checkProvider();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isCheckingProvider && !hasProvider && !checkProviderError && !hasTrackedOnboardingStart.current) {
|
||||||
trackOnboardingStarted();
|
trackOnboardingStarted();
|
||||||
hasTrackedOnboardingStart.current = true;
|
hasTrackedOnboardingStart.current = true;
|
||||||
}
|
}
|
||||||
}, [isCheckingProvider, hasProvider]);
|
}, [isCheckingProvider, hasProvider, checkProviderError]);
|
||||||
|
|
||||||
const handleConfigured = async (providerName: string, modelId?: string) => {
|
const handleConfigured = async (providerName: string, modelId?: string) => {
|
||||||
trackOnboardingProviderSelected({ provider: providerName });
|
trackOnboardingProviderSelected({ provider: providerName });
|
||||||
@@ -107,6 +131,23 @@ export default function OnboardingGuard({ children }: OnboardingGuardProps) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkProviderError) {
|
||||||
|
return (
|
||||||
|
<div className="h-screen w-full bg-background-default flex flex-col items-center justify-center">
|
||||||
|
<div className="text-center max-w-md">
|
||||||
|
<div className="mb-4">
|
||||||
|
<Goose className="size-8 mx-auto" />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-xl font-light mb-3">{intl.formatMessage(i18n.checkProviderErrorTitle)}</h1>
|
||||||
|
<p className="text-text-muted mb-6">{intl.formatMessage(i18n.checkProviderErrorDescription)}</p>
|
||||||
|
<Button onClick={() => checkProvider()}>
|
||||||
|
{intl.formatMessage(i18n.retry)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasProvider) {
|
if (hasProvider) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2387,6 +2387,15 @@
|
|||||||
"navigationStyleSelector.tileLabel": {
|
"navigationStyleSelector.tileLabel": {
|
||||||
"defaultMessage": "Tile"
|
"defaultMessage": "Tile"
|
||||||
},
|
},
|
||||||
|
"onboardingGuard.checkProviderErrorDescription": {
|
||||||
|
"defaultMessage": "The server may be starting up or temporarily unavailable."
|
||||||
|
},
|
||||||
|
"onboardingGuard.checkProviderErrorTitle": {
|
||||||
|
"defaultMessage": "Unable to connect to Goose server"
|
||||||
|
},
|
||||||
|
"onboardingGuard.retry": {
|
||||||
|
"defaultMessage": "Retry"
|
||||||
|
},
|
||||||
"onboardingGuard.welcomeDescription": {
|
"onboardingGuard.welcomeDescription": {
|
||||||
"defaultMessage": "Your local AI agent. Connect an AI model provider to get started."
|
"defaultMessage": "Your local AI agent. Connect an AI model provider to get started."
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user