Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions ui/desktop/src/components/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
ExtensionQuery,
ProviderDetails,
} from '../api/types.gen';
import { getSecretKey } from '../config';
Comment thread
lilydelalande marked this conversation as resolved.
Outdated
Comment thread
lilydelalande marked this conversation as resolved.
Outdated

// Initialize client configuration
client.setConfig({
Expand Down Expand Up @@ -71,13 +72,13 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
setConfig(response.data.config || {});
};

const upsert = async (key: string, value: unknown, isSecret?: boolean) => {
const upsert = async (key: string, value: unknown, isSecret: boolean = false) => {
console.log('trying to upsert', key, value, isSecret);
const query: UpsertConfigQuery = {
key,
value,
is_secret: isSecret || null,
key: key,
value: value,
is_secret: isSecret,
};

await upsertConfig({
body: query,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,37 @@ import ProviderSetupActions from './subcomponents/ProviderSetupActions';
import ProviderLogo from './subcomponents/ProviderLogo';
import { useProviderModal } from './ProviderModalProvider';
import { SecureStorageNotice } from './subcomponents/SecureStorageNotice';
import DefaultSubmitHandler from './subcomponents/handlers/DefaultSubmitHandler';
import { DefaultSubmitHandler } from './subcomponents/handlers/DefaultSubmitHandler';
import OllamaSubmitHandler from './subcomponents/handlers/OllamaSubmitHandler';
import OllamaForm from './subcomponents/forms/OllamaForm';
import { useConfig } from '../../../ConfigContext';

const customSubmitHandler = {
const customSubmitHandlerMap = {
provider_name: OllamaSubmitHandler, // example
};

const customForms = {
const customFormsMap = {
provider_name: OllamaForm, // example
};

export default function ProviderConfigurationModal() {
const { upsert } = useConfig();
const { isOpen, currentProvider, modalProps, closeModal } = useProviderModal();
const [configValues, setConfigValues] = useState({});

useEffect(() => {
if (currentProvider) {
// Initialize form with default values
const initialValues = {};
// FIXME
// if (currentProvider.parameters) {
// currentProvider.parameters.forEach((param) => {
// initialValues[param.name] = param.default || '';
// });
// }
setConfigValues(initialValues);
} else {
setConfigValues({});
}
}, [currentProvider]);

if (!isOpen || !currentProvider) return null;

const headerText = `Configure ${currentProvider.metadata.display_name}`;
const descriptionText = `Add your API key(s) for this provider to integrate into Goose`;

const SubmitHandler = customSubmitHandler[currentProvider.name] || DefaultSubmitHandler;
const FormComponent = customForms[currentProvider.name] || DefaultProviderSetupForm;
const SubmitHandler = customSubmitHandlerMap[currentProvider.name] || DefaultSubmitHandler;
const FormComponent = customFormsMap[currentProvider.name] || DefaultProviderSetupForm;

const handleSubmitForm = (e) => {
e.preventDefault();
console.log('Form submitted for:', currentProvider.name);

SubmitHandler(configValues);
SubmitHandler(upsert, currentProvider, configValues);

// Close the modal unless the custom handler explicitly returns false
// This gives custom handlers the ability to keep the modal open if needed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ export default function DefaultProviderSetupForm({
) : (
requiredParameters.map((parameter) => (
<div key={parameter.name}>
<label className="block text-sm font-medium text-gray-700 mb-1">
{parameter.name}
<span className="text-red-500 ml-1">*</span>
</label>
<label className="block text-sm font-medium text-gray-700 mb-1">{parameter.name}</label>
<Input
type={parameter.secret ? 'password' : 'text'}
value={configValues[parameter.name] || ''}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,114 @@
export default function DefaultSubmitHandler(configValues) {
// Log each field value individually for clarity
console.log('Field values:');
Object.entries(configValues).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
import { useConfig } from '../../../../../ConfigContext';
import React from 'react';

/**
* Custom hook for provider configuration submission
* Returns a submit handler function and submission state
*/
export const useDefaultSubmit = () => {
const { upsert } = useConfig();
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [error, setError] = React.useState(null);
const [isSuccess, setIsSuccess] = React.useState(false);

/**
* Submit handler for provider configuration
* @param {Object} provider - The provider object with metadata
* @param {Object} configValues - The form values to be submitted
* @param {Function} onSuccess - Optional callback for successful submission
*/
const handleSubmit = async (provider, configValues, onSuccess) => {
setIsSubmitting(true);
setError(null);
setIsSuccess(false);

try {
const parameters = provider.metadata.config_keys || [];

// Create an array of promises for all the upsert operations
const upsertPromises = parameters.map((parameter) => {
// Skip parameters that don't have a value and aren't required
if (!configValues[parameter.name] && !parameter.required) {
return Promise.resolve();
}

// 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();
}

// Create the provider-specific config key
// Format: provider.{provider_name}.{parameter_name}
const configKey = `provider.${provider.name}.${parameter.name}`;

// Pass the is_secret flag from the parameter definition
return upsert(configKey, value, parameter.secret || false);
});

// Wait for all upsert operations to complete
await Promise.all(upsertPromises);

setIsSuccess(true);

// Call the success callback if provided
if (onSuccess) {
onSuccess();
}
} catch (err) {
console.error('Failed to save provider configuration:', err);
setError('Failed to save configuration. Please try again.');
} finally {
setIsSubmitting(false);
}
};

return {
handleSubmit,
isSubmitting,
error,
isSuccess,
};
};

/**
* Standalone function to submit provider configuration
* Useful for components that don't want to use the hook
*/
export const DefaultSubmitHandler = async (upsertFn, provider, configValues) => {
const parameters = provider.metadata.config_keys || [];

const upsertPromises = parameters.map((parameter) => {
// Skip parameters that don't have a value and aren't required
if (!configValues[parameter.name] && !parameter.required) {
return Promise.resolve();
}

// 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();
}

// Create the provider-specific config key
const configKey = `${parameter.name}`;

// Explicitly define is_secret as a boolean (true/false) or null
// This is critical for Rust's Option<bool> type
const isSecret = parameter.secret === true;

// Pass the is_secret flag from the parameter definition
return upsertFn(configKey, value, isSecret);
});
}

// Wait for all upsert operations to complete
return Promise.all(upsertPromises);
};