import React, { memo, useMemo, useCallback, useState } from 'react'; import { ProviderCard } from './subcomponents/ProviderCard'; import CardContainer from './subcomponents/CardContainer'; import ProviderConfigurationModal from './modal/ProviderConfigurationModal'; import type { CustomProviderConfigDto } from '@aaif/goose-sdk'; import type { ProviderDetails, UpdateCustomProviderRequest } from '../../../types/providers'; import { acpCreateCustomProviderFromRequest, acpGetCustomProvider, acpDeleteCustomProvider, acpUpdateCustomProviderFromRequest, } from '../../../acp/providers'; import { Plus } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog'; import CustomProviderForm from './modal/subcomponents/forms/CustomProviderForm'; import { SwitchModelModal } from '../models/subcomponents/SwitchModelModal'; import { useModelAndProvider } from '../../ModelAndProviderContext'; import type { View } from '../../../utils/navigationUtils'; import { defineMessages, useIntl } from '../../../i18n'; const i18n = defineMessages({ addProvider: { id: 'providerGrid.addProvider', defaultMessage: 'Add Provider', }, fromTemplateOrManual: { id: 'providerGrid.fromTemplateOrManual', defaultMessage: 'From template or manual setup', }, editProvider: { id: 'providerGrid.editProvider', defaultMessage: 'Edit Provider', }, configureProvider: { id: 'providerGrid.configureProvider', defaultMessage: 'Configure Provider', }, addProviderTitle: { id: 'providerGrid.addProviderTitle', defaultMessage: 'Add Provider', }, chooseModel: { id: 'providerGrid.chooseModel', defaultMessage: 'Choose Model', }, }); const GridLayout = memo(function GridLayout({ children }: { children: React.ReactNode }) { return (
{children}
); }); const CustomProviderCard = memo(function CustomProviderCard({ onClick }: { onClick: () => void }) { const intl = useIntl(); return (
{intl.formatMessage(i18n.addProvider)}
{intl.formatMessage(i18n.fromTemplateOrManual)}
} grayedOut={false} borderStyle="dashed" /> ); }); function ProviderCards({ providers, isOnboarding, refreshProviders, setView, onModelSelected, }: { providers: ProviderDetails[]; isOnboarding: boolean; refreshProviders?: () => void; setView?: (view: View) => void; onModelSelected?: (model?: string) => void; }) { const intl = useIntl(); const [configuringProvider, setConfiguringProvider] = useState(null); const [showCustomProviderModal, setShowCustomProviderModal] = useState(false); const [showSwitchModelModal, setShowSwitchModelModal] = useState(false); const [switchModelProvider, setSwitchModelProvider] = useState(null); const [isActiveProvider, setIsActiveProvider] = useState(false); const { getCurrentModelAndProvider } = useModelAndProvider(); const [editingProvider, setEditingProvider] = useState<{ id: string; config: CustomProviderConfigDto; isEditable: boolean; providerType: string; } | null>(null); const handleProviderLaunchWithModelSelection = useCallback((provider: ProviderDetails) => { setSwitchModelProvider(provider.name); setShowSwitchModelModal(true); }, []); const openModal = useCallback( (provider: ProviderDetails) => setConfiguringProvider(provider), [] ); const configureProviderViaModal = useCallback( async (provider: ProviderDetails) => { if (provider.provider_type === 'Custom') { const result = await acpGetCustomProvider(provider.name); if (result) { setEditingProvider({ id: provider.name, config: result.provider, isEditable: result.editable, providerType: provider.provider_type, }); // Check if this is the active provider try { const providerModel = await getCurrentModelAndProvider(); setIsActiveProvider(provider.name === providerModel.provider); } catch { setIsActiveProvider(false); } setShowCustomProviderModal(true); } } else { openModal(provider); } }, [openModal, getCurrentModelAndProvider] ); const handleUpdateCustomProvider = useCallback( async (data: UpdateCustomProviderRequest) => { if (!editingProvider) return; await acpUpdateCustomProviderFromRequest(editingProvider.id, data); const providerId = editingProvider.id; setShowCustomProviderModal(false); setEditingProvider(null); if (refreshProviders) { refreshProviders(); } setSwitchModelProvider(providerId); setShowSwitchModelModal(true); }, [editingProvider, refreshProviders] ); const handleDeleteCustomProvider = useCallback(async () => { if (!editingProvider) return; await acpDeleteCustomProvider(editingProvider.id); setShowCustomProviderModal(false); setEditingProvider(null); setIsActiveProvider(false); if (refreshProviders) { refreshProviders(); } }, [editingProvider, refreshProviders]); const handleCloseModal = useCallback(() => { setShowCustomProviderModal(false); setEditingProvider(null); setIsActiveProvider(false); }, []); const onCloseProviderConfig = useCallback(() => { setConfiguringProvider(null); if (refreshProviders) { refreshProviders(); } }, [refreshProviders]); const onProviderConfigured = useCallback( async (provider: ProviderDetails) => { setConfiguringProvider(null); if (refreshProviders) { await refreshProviders(); } setSwitchModelProvider(provider.name); setShowSwitchModelModal(true); }, [refreshProviders] ); const onCloseSwitchModelModal = useCallback(() => { setShowSwitchModelModal(false); }, []); const handleSetView = useCallback( (view: View) => { setShowSwitchModelModal(false); if (setView) { setView(view); } }, [setView] ); const handleCreateCustomProvider = useCallback( async (data: UpdateCustomProviderRequest) => { const result = await acpCreateCustomProviderFromRequest(data); const providerId = result.provider_name; setShowCustomProviderModal(false); if (refreshProviders) { await refreshProviders(); } setSwitchModelProvider(providerId); setShowSwitchModelModal(true); }, [refreshProviders] ); const providerCards = useMemo(() => { // providers needs to be an array const providersArray = Array.isArray(providers) ? providers : []; // Sort providers alphabetically by display name const sortedProviders = [...providersArray].sort((a, b) => a.metadata.display_name.localeCompare(b.metadata.display_name) ); const cards = sortedProviders.map((provider) => ( configureProviderViaModal(provider)} onLaunch={() => handleProviderLaunchWithModelSelection(provider)} isOnboarding={isOnboarding} /> )); cards.push( setShowCustomProviderModal(true)} /> ); return cards; }, [providers, isOnboarding, configureProviderViaModal, handleProviderLaunchWithModelSelection]); const initialData = editingProvider && { engine: editingProvider.config.engine, display_name: editingProvider.config.displayName, api_url: editingProvider.config.apiUrl, base_path: editingProvider.config.basePath ?? undefined, api_key: '', models: editingProvider.config.models ?? [], supports_streaming: editingProvider.config.supportsStreaming ?? true, requires_auth: editingProvider.config.requiresAuth ?? true, headers: editingProvider.config.headers ?? undefined, catalog_provider_id: editingProvider.config.catalogProviderId ?? undefined, }; const editable = editingProvider ? editingProvider.isEditable : true; const title = editingProvider ? editable ? intl.formatMessage(i18n.editProvider) : intl.formatMessage(i18n.configureProvider) : intl.formatMessage(i18n.addProviderTitle); return ( <> {providerCards} {title} {configuringProvider && ( )} {showSwitchModelModal && ( )} ); } export default function ProviderGrid({ providers, isOnboarding, refreshProviders, setView, onModelSelected, }: { providers: ProviderDetails[]; isOnboarding: boolean; refreshProviders?: () => void; setView?: (view: View) => void; onModelSelected?: (model?: string) => void; }) { return ( ); }