Better search paths and handling of CLI providers (#5554)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { ScrollArea } from '../../ui/scroll-area';
|
||||
import BackButton from '../../ui/BackButton';
|
||||
import ProviderGrid from './ProviderGrid';
|
||||
import { useConfig } from '../../ConfigContext';
|
||||
import { ProviderDetails } from '../../../api';
|
||||
import { ProviderDetails, setConfigProvider } from '../../../api';
|
||||
import { toastService } from '../../../toasts';
|
||||
|
||||
interface ProviderSettingsProps {
|
||||
@@ -12,7 +12,7 @@ interface ProviderSettingsProps {
|
||||
}
|
||||
|
||||
export default function ProviderSettings({ onClose, isOnboarding }: ProviderSettingsProps) {
|
||||
const { getProviders, upsert } = useConfig();
|
||||
const { getProviders } = useConfig();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [providers, setProviders] = useState<ProviderDetails[]>([]);
|
||||
const initialLoadDone = useRef(false);
|
||||
@@ -56,15 +56,13 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
||||
const model = provider.metadata.default_model;
|
||||
|
||||
try {
|
||||
// update the config
|
||||
// set GOOSE_PROVIDER in the config file
|
||||
upsert('GOOSE_PROVIDER', provider_name, false).then((_) =>
|
||||
console.log('Setting GOOSE_PROVIDER to', provider_name)
|
||||
);
|
||||
// set GOOSE_MODEL in the config file
|
||||
upsert('GOOSE_MODEL', model, false).then((_) =>
|
||||
console.log('Setting GOOSE_MODEL to', model)
|
||||
);
|
||||
await setConfigProvider({
|
||||
body: {
|
||||
provider: provider_name,
|
||||
model,
|
||||
},
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
toastService.configure({ silent: false });
|
||||
toastService.success({
|
||||
@@ -85,7 +83,7 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
||||
});
|
||||
}
|
||||
},
|
||||
[onClose, upsert]
|
||||
[onClose]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,6 +18,7 @@ import { useConfig } from '../../../ConfigContext';
|
||||
import { useModelAndProvider } from '../../../ModelAndProviderContext';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import { ProviderDetails, removeCustomProvider } from '../../../../api';
|
||||
import { Button } from '../../../../components/ui/button';
|
||||
|
||||
interface ProviderConfigurationModalProps {
|
||||
provider: ProviderDetails;
|
||||
@@ -34,6 +35,7 @@ export default function ProviderConfigurationModal({
|
||||
const [configValues, setConfigValues] = useState<Record<string, ConfigInput>>({});
|
||||
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
||||
const [isActiveProvider, setIsActiveProvider] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const requiredParameters = provider.metadata.config_keys.filter(
|
||||
(param) => param.required === true
|
||||
@@ -79,8 +81,12 @@ export default function ProviderConfigurationModal({
|
||||
.map(([k, entry]) => [k, entry.value || ''])
|
||||
);
|
||||
|
||||
await providerConfigSubmitHandler(upsert, provider, toSubmit);
|
||||
onClose();
|
||||
try {
|
||||
await providerConfigSubmitHandler(upsert, provider, toSubmit);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
setError(`${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -137,54 +143,71 @@ export default function ProviderConfigurationModal({
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
{getModalIcon()}
|
||||
{headerText}
|
||||
</DialogTitle>
|
||||
<DialogDescription>{descriptionText}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<>
|
||||
<Dialog open={!!error} onOpenChange={(open) => !open && setError(null)}>
|
||||
<DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
|
||||
<DialogTitle className="flex items-center gap-2">Error</DialogTitle>
|
||||
<DialogDescription className="text-inherit text-base">
|
||||
There was an error checking this provider configuration.
|
||||
</DialogDescription>
|
||||
<pre className="ml-2">{error}</pre>
|
||||
<div>Check your configuration again to use this provider.</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setError(null)}>
|
||||
Go Back
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog open={!error} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
{getModalIcon()}
|
||||
{headerText}
|
||||
</DialogTitle>
|
||||
<DialogDescription>{descriptionText}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-4">
|
||||
{/* Contains information used to set up each provider */}
|
||||
{/* Only show the form when NOT in delete confirmation mode */}
|
||||
{!showDeleteConfirmation ? (
|
||||
<>
|
||||
{/* Contains information used to set up each provider */}
|
||||
<DefaultProviderSetupForm
|
||||
configValues={configValues}
|
||||
setConfigValues={setConfigValues}
|
||||
provider={provider}
|
||||
validationErrors={validationErrors}
|
||||
/>
|
||||
<div className="py-4">
|
||||
{/* Contains information used to set up each provider */}
|
||||
{/* Only show the form when NOT in delete confirmation mode */}
|
||||
{!showDeleteConfirmation ? (
|
||||
<>
|
||||
{/* Contains information used to set up each provider */}
|
||||
<DefaultProviderSetupForm
|
||||
configValues={configValues}
|
||||
setConfigValues={setConfigValues}
|
||||
provider={provider}
|
||||
validationErrors={validationErrors}
|
||||
/>
|
||||
|
||||
{requiredParameters.length > 0 &&
|
||||
provider.metadata.config_keys &&
|
||||
provider.metadata.config_keys.length > 0 && <SecureStorageNotice />}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
{requiredParameters.length > 0 &&
|
||||
provider.metadata.config_keys &&
|
||||
provider.metadata.config_keys.length > 0 && <SecureStorageNotice />}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<ProviderSetupActions
|
||||
requiredParameters={requiredParameters}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={handleSubmitForm}
|
||||
onDelete={handleDelete}
|
||||
showDeleteConfirmation={showDeleteConfirmation}
|
||||
onConfirmDelete={handleConfirmDelete}
|
||||
onCancelDelete={() => {
|
||||
setIsActiveProvider(false);
|
||||
setShowDeleteConfirmation(false);
|
||||
}}
|
||||
canDelete={isConfigured && !isActiveProvider}
|
||||
providerName={provider.metadata.display_name}
|
||||
isActiveProvider={isActiveProvider}
|
||||
/>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<DialogFooter>
|
||||
<ProviderSetupActions
|
||||
requiredParameters={requiredParameters}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={handleSubmitForm}
|
||||
onDelete={handleDelete}
|
||||
showDeleteConfirmation={showDeleteConfirmation}
|
||||
onConfirmDelete={handleConfirmDelete}
|
||||
onCancelDelete={() => {
|
||||
setIsActiveProvider(false);
|
||||
setShowDeleteConfirmation(false);
|
||||
}}
|
||||
canDelete={isConfigured && !isActiveProvider}
|
||||
providerName={provider.metadata.display_name}
|
||||
isActiveProvider={isActiveProvider}
|
||||
/>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+18
-25
@@ -1,3 +1,5 @@
|
||||
import { checkProvider } from '../../../../../../api';
|
||||
|
||||
/**
|
||||
* Standalone function to submit provider configuration
|
||||
* Useful for components that don't want to use the hook
|
||||
@@ -19,15 +21,6 @@ export const providerConfigSubmitHandler = async (
|
||||
) => {
|
||||
const parameters = provider.metadata.config_keys || [];
|
||||
|
||||
if (parameters.length === 0) {
|
||||
// For zero-config providers, mark them as configured
|
||||
const configKey = `${provider.name}_configured`;
|
||||
await upsertFn(configKey, true, false);
|
||||
|
||||
await upsertFn('GOOSE_PROVIDER', provider.name, false);
|
||||
return;
|
||||
}
|
||||
|
||||
const requiredParams = parameters.filter((param) => param.required);
|
||||
if (requiredParams.length === 0 && parameters.length > 0) {
|
||||
const allOptionalWithDefaults = parameters.every(
|
||||
@@ -35,8 +28,6 @@ export const providerConfigSubmitHandler = async (
|
||||
);
|
||||
if (allOptionalWithDefaults) {
|
||||
const promises: Promise<void>[] = [];
|
||||
const configKey = `${provider.name}_configured`;
|
||||
promises.push(upsertFn(configKey, true, false));
|
||||
|
||||
for (const param of parameters) {
|
||||
if (param.default !== undefined) {
|
||||
@@ -46,39 +37,41 @@ export const providerConfigSubmitHandler = async (
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
await Promise.all(promises);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const upsertPromises = parameters.map(
|
||||
(parameter: { name: string; required?: boolean; default?: unknown; secret?: boolean }) => {
|
||||
// Skip parameters that don't have a value and aren't required
|
||||
async (parameter: {
|
||||
name: string;
|
||||
required?: boolean;
|
||||
default?: unknown;
|
||||
secret?: boolean;
|
||||
}) => {
|
||||
if (!configValues[parameter.name] && !parameter.required) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// For required parameters with no value, use the default if available
|
||||
const value =
|
||||
configValues[parameter.name] !== undefined
|
||||
? configValues[parameter.name]
|
||||
: parameter.default;
|
||||
|
||||
// Skip if there's still no value
|
||||
if (value === undefined || value === null) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the provider-specific config key
|
||||
const configKey = `${parameter.name}`;
|
||||
|
||||
// Explicitly define is_secret as a boolean (true/false)
|
||||
const isSecret = parameter.secret === true;
|
||||
|
||||
// Pass the is_secret flag from the parameter definition
|
||||
return upsertFn(configKey, value, isSecret);
|
||||
await upsertFn(configKey, value, isSecret);
|
||||
}
|
||||
);
|
||||
|
||||
// Wait for all upsert operations to complete
|
||||
return Promise.all(upsertPromises);
|
||||
await Promise.all(upsertPromises);
|
||||
await checkProvider({
|
||||
body: { provider: provider.name },
|
||||
throwOnError: true,
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user