Better search paths and handling of CLI providers (#5554)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -382,6 +382,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/config/check_provider": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"super::routes::config_management"
|
||||
],
|
||||
"operationId": "check_provider",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CheckProviderRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/config/custom-providers": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -840,6 +859,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/config/set_provider": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"super::routes::config_management"
|
||||
],
|
||||
"operationId": "set_config_provider",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SetProviderRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/config/upsert": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -2152,6 +2190,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CheckProviderRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"properties": {
|
||||
"provider": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigKey": {
|
||||
"type": "object",
|
||||
"description": "Configuration key metadata for provider setup",
|
||||
@@ -4359,6 +4408,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SetProviderRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"provider",
|
||||
"model"
|
||||
],
|
||||
"properties": {
|
||||
"model": {
|
||||
"type": "string"
|
||||
},
|
||||
"provider": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Settings": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type { Client, Options as Options2, TDataShape } from './client';
|
||||
import { client } from './client.gen';
|
||||
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, ConfirmPermissionData, ConfirmPermissionErrors, ConfirmPermissionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetToolsData, GetToolsErrors, GetToolsResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StatusData, StatusResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
|
||||
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CheckProviderData, ConfirmPermissionData, ConfirmPermissionErrors, ConfirmPermissionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetToolsData, GetToolsErrors, GetToolsResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StatusData, StatusResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<TData, ThrowOnError> & {
|
||||
/**
|
||||
@@ -116,6 +116,17 @@ export const backupConfig = <ThrowOnError extends boolean = false>(options?: Opt
|
||||
});
|
||||
};
|
||||
|
||||
export const checkProvider = <ThrowOnError extends boolean = false>(options: Options<CheckProviderData, ThrowOnError>) => {
|
||||
return (options.client ?? client).post<unknown, unknown, ThrowOnError>({
|
||||
url: '/config/check_provider',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const createCustomProvider = <ThrowOnError extends boolean = false>(options: Options<CreateCustomProviderData, ThrowOnError>) => {
|
||||
return (options.client ?? client).post<CreateCustomProviderResponses, CreateCustomProviderErrors, ThrowOnError>({
|
||||
url: '/config/custom-providers',
|
||||
@@ -238,6 +249,17 @@ export const removeConfig = <ThrowOnError extends boolean = false>(options: Opti
|
||||
});
|
||||
};
|
||||
|
||||
export const setConfigProvider = <ThrowOnError extends boolean = false>(options: Options<SetConfigProviderData, ThrowOnError>) => {
|
||||
return (options.client ?? client).post<unknown, unknown, ThrowOnError>({
|
||||
url: '/config/set_provider',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const upsertConfig = <ThrowOnError extends boolean = false>(options: Options<UpsertConfigData, ThrowOnError>) => {
|
||||
return (options.client ?? client).post<UpsertConfigResponses, UpsertConfigErrors, ThrowOnError>({
|
||||
url: '/config/upsert',
|
||||
|
||||
@@ -32,6 +32,10 @@ export type ChatRequest = {
|
||||
session_id: string;
|
||||
};
|
||||
|
||||
export type CheckProviderRequest = {
|
||||
provider: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration key metadata for provider setup
|
||||
*/
|
||||
@@ -731,6 +735,11 @@ export type SessionsQuery = {
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export type SetProviderRequest = {
|
||||
model: string;
|
||||
provider: string;
|
||||
};
|
||||
|
||||
export type Settings = {
|
||||
goose_model?: string | null;
|
||||
goose_provider?: string | null;
|
||||
@@ -1212,6 +1221,13 @@ export type BackupConfigResponses = {
|
||||
|
||||
export type BackupConfigResponse = BackupConfigResponses[keyof BackupConfigResponses];
|
||||
|
||||
export type CheckProviderData = {
|
||||
body: CheckProviderRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/config/check_provider';
|
||||
};
|
||||
|
||||
export type CreateCustomProviderData = {
|
||||
body: UpdateCustomProviderRequest;
|
||||
path?: never;
|
||||
@@ -1578,6 +1594,13 @@ export type RemoveConfigResponses = {
|
||||
|
||||
export type RemoveConfigResponse = RemoveConfigResponses[keyof RemoveConfigResponses];
|
||||
|
||||
export type SetConfigProviderData = {
|
||||
body: SetProviderRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/config/set_provider';
|
||||
};
|
||||
|
||||
export type UpsertConfigData = {
|
||||
body: UpsertConfigQuery;
|
||||
path?: never;
|
||||
|
||||
@@ -34,7 +34,7 @@ export function ErrorUI({ error }: { error: Error }) {
|
||||
</p>
|
||||
)}
|
||||
|
||||
<pre className="text-destructive text-sm dark:text-white p-4 bg-muted rounded-lg w-full overflow-auto border border-border">
|
||||
<pre className="text-destructive text-sm dark:text-white p-4 bg-muted rounded-lg w-full overflow-auto border border-border whitespace-pre-wrap">
|
||||
{error.message}
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import { toastError, toastSuccess } from '../toasts';
|
||||
import Model, { getProviderMetadata } from './settings/models/modelInterface';
|
||||
import { ProviderMetadata, updateAgentProvider } from '../api';
|
||||
import { ProviderMetadata, setConfigProvider, updateAgentProvider } from '../api';
|
||||
import { useConfig } from './ConfigContext';
|
||||
import {
|
||||
getModelDisplayName,
|
||||
@@ -12,10 +12,6 @@ import {
|
||||
export const UNKNOWN_PROVIDER_TITLE = 'Provider name lookup';
|
||||
|
||||
// errors
|
||||
const CHANGE_MODEL_ERROR_TITLE = 'Change failed';
|
||||
const SWITCH_MODEL_AGENT_ERROR_MSG =
|
||||
'Failed to start agent with selected model -- please try again';
|
||||
const CONFIG_UPDATE_ERROR_MSG = 'Failed to update configuration settings -- please try again';
|
||||
export const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
||||
|
||||
// success
|
||||
@@ -43,61 +39,68 @@ const ModelAndProviderContext = createContext<ModelAndProviderContextType | unde
|
||||
export const ModelAndProviderProvider: React.FC<ModelAndProviderProviderProps> = ({ children }) => {
|
||||
const [currentModel, setCurrentModel] = useState<string | null>(null);
|
||||
const [currentProvider, setCurrentProvider] = useState<string | null>(null);
|
||||
const { read, upsert, getProviders } = useConfig();
|
||||
const { read, getProviders } = useConfig();
|
||||
|
||||
const changeModel = useCallback(
|
||||
async (sessionId: string | null, model: Model) => {
|
||||
const modelName = model.name;
|
||||
const providerName = model.provider;
|
||||
let phase = 'agent';
|
||||
const changeModel = useCallback(async (sessionId: string | null, model: Model) => {
|
||||
const modelName = model.name;
|
||||
const providerName = model.provider;
|
||||
let phase = 'agent';
|
||||
|
||||
try {
|
||||
if (sessionId) {
|
||||
await updateAgentProvider({
|
||||
body: {
|
||||
session_id: sessionId,
|
||||
provider: providerName,
|
||||
model: modelName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
phase = 'config';
|
||||
await upsert('GOOSE_PROVIDER', providerName, false);
|
||||
await upsert('GOOSE_MODEL', modelName, false);
|
||||
|
||||
setCurrentProvider(providerName);
|
||||
setCurrentModel(modelName);
|
||||
|
||||
toastSuccess({
|
||||
title: CHANGE_MODEL_TOAST_TITLE,
|
||||
msg: `${SWITCH_MODEL_SUCCESS_MSG} -- using ${model.alias ?? modelName} from ${model.subtext ?? providerName}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to change model at ${phase} step -- ${modelName} ${providerName}`);
|
||||
toastError({
|
||||
title: CHANGE_MODEL_ERROR_TITLE,
|
||||
msg: phase === 'agent' ? SWITCH_MODEL_AGENT_ERROR_MSG : CONFIG_UPDATE_ERROR_MSG,
|
||||
traceback: error instanceof Error ? error.message : String(error),
|
||||
try {
|
||||
if (sessionId) {
|
||||
await updateAgentProvider({
|
||||
body: {
|
||||
session_id: sessionId,
|
||||
provider: providerName,
|
||||
model: modelName,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[upsert]
|
||||
);
|
||||
|
||||
phase = 'config';
|
||||
await setConfigProvider({
|
||||
body: {
|
||||
provider: providerName,
|
||||
model: modelName,
|
||||
},
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
setCurrentProvider(providerName);
|
||||
setCurrentModel(modelName);
|
||||
|
||||
toastSuccess({
|
||||
title: CHANGE_MODEL_TOAST_TITLE,
|
||||
msg: `${SWITCH_MODEL_SUCCESS_MSG} -- using ${model.alias ?? modelName} from ${model.subtext ?? providerName}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to change model at ${phase} step -- ${modelName} ${providerName}`);
|
||||
toastError({
|
||||
title: `${providerName}/${modelName} failed`,
|
||||
msg: `${error}`,
|
||||
traceback: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getFallbackModelAndProvider = useCallback(async () => {
|
||||
const provider = window.appConfig.get('GOOSE_DEFAULT_PROVIDER') as string;
|
||||
const model = window.appConfig.get('GOOSE_DEFAULT_MODEL') as string;
|
||||
if (provider && model) {
|
||||
try {
|
||||
await upsert('GOOSE_MODEL', model, false);
|
||||
await upsert('GOOSE_PROVIDER', provider, false);
|
||||
await setConfigProvider({
|
||||
body: {
|
||||
provider: provider,
|
||||
model: model,
|
||||
},
|
||||
throwOnError: true,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[getFallbackModelAndProvider] Failed to write to config', error);
|
||||
}
|
||||
}
|
||||
return { model: model, provider: provider };
|
||||
}, [upsert]);
|
||||
}, []);
|
||||
|
||||
const getCurrentModelAndProvider = useCallback(async () => {
|
||||
let model: string;
|
||||
|
||||
@@ -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