diff --git a/src/common/components/AppBreadcrumbs.tsx b/src/common/components/AppBreadcrumbs.tsx index 7eabf24e3..a1a53dd2c 100644 --- a/src/common/components/AppBreadcrumbs.tsx +++ b/src/common/components/AppBreadcrumbs.tsx @@ -9,6 +9,7 @@ import { Link } from '~/common/components/Link'; const _sx = { p: 0 }; export function AppBreadcrumbs(props: { + size?: 'sm' | 'md' | 'lg'; children?: React.ReactNode; rootTitle?: string; onRootClick?: () => void; @@ -22,7 +23,7 @@ export function AppBreadcrumbs(props: { onRootClick?.(); }, [onRootClick]); - return } aria-label='breadcrumbs' sx={_sx}> + return } aria-label='breadcrumbs' sx={_sx}> {(props.children && !!rootTitle && !!onRootClick) ? {props.rootTitle} : {props.rootTitle} diff --git a/src/common/components/forms/FormInputKey.tsx b/src/common/components/forms/FormInputKey.tsx index af1b84672..058bb9b07 100644 --- a/src/common/components/forms/FormInputKey.tsx +++ b/src/common/components/forms/FormInputKey.tsx @@ -43,6 +43,7 @@ export function FormInputKey(props: { key={acId} name={acId} autoComplete='off' + autoFocus={!props.required ? undefined : props.value ? undefined : true} // autoComplete={props.noKey ? 'off' : 'new-password'} variant={props.required ? 'outlined' : 'outlined' /* 'soft */} value={props.value} onChange={handleChange} diff --git a/src/common/components/forms/SetupFormRefetchButton.tsx b/src/common/components/forms/SetupFormRefetchButton.tsx index 0aaa18a54..df9a25876 100644 --- a/src/common/components/forms/SetupFormRefetchButton.tsx +++ b/src/common/components/forms/SetupFormRefetchButton.tsx @@ -23,7 +23,7 @@ export function SetupFormRefetchButton(props: { {props.leftButton} {!!props.advanced && ( - + {props.advanced.on ? 'Hide Advanced' : 'Advanced'} )} diff --git a/src/modules/llms/models-modal/ModelsModal.tsx b/src/modules/llms/models-modal/ModelsModal.tsx index 6f87db3dc..2c44f1f99 100644 --- a/src/modules/llms/models-modal/ModelsModal.tsx +++ b/src/modules/llms/models-modal/ModelsModal.tsx @@ -3,18 +3,18 @@ import * as React from 'react'; import { Box, Button, Divider } from '@mui/joy'; import type { DModelsService } from '~/common/stores/llms/modelsservice.types'; +import { AppBreadcrumbs } from '~/common/components/AppBreadcrumbs'; import { GoodModal } from '~/common/components/modals/GoodModal'; -import { llmsStoreState } from '~/common/stores/llms/store-llms'; import { optimaActions, optimaOpenModels, useOptimaModelsModalsState } from '~/common/layout/optima/useOptima'; import { runWhenIdle } from '~/common/util/pwaUtils'; -import { useIsMobile } from '~/common/components/useMatchMedia'; import { useHasLLMs, useModelsServices } from '~/common/stores/llms/llms.hooks'; +import { useIsMobile } from '~/common/components/useMatchMedia'; import { LLMOptionsModal } from './LLMOptionsModal'; import { ModelsList } from './ModelsList'; import { ModelsServiceSelector } from './ModelsServiceSelector'; import { ModelsWizard } from './ModelsWizard'; -import { findModelVendor, getDefaultModelVendor } from '../vendors/vendors.registry'; +import { findModelVendor } from '../vendors/vendors.registry'; // configuration @@ -62,13 +62,20 @@ function ModelsConfiguratorModal(props: { // Auto-add the default service - at boot, when no service is present - const autoAddTrigger = !showWizard && props.allowAutoTrigger; + // const autoAddTrigger = !showWizard && props.allowAutoTrigger; + // React.useEffect(() => { + // // Note: we use the immediate version to not react to deletions + // const { createModelsService, sources: modelsServices } = llmsStoreState(); + // if (autoAddTrigger && !modelsServices.length) + // createModelsService(getDefaultModelVendor()); + // }, [autoAddTrigger]); + + // [effect] Re-trigger easy mode when going back to 0 services + const triggerWizard = !modelsServices.length; React.useEffect(() => { - // Note: we use the immediate version to not react to deletions - const { createModelsService, sources: modelsServices } = llmsStoreState(); - if (autoAddTrigger && !modelsServices.length) - createModelsService(getDefaultModelVendor()); - }, [autoAddTrigger]); + if (triggerWizard) + setShowWizard(true); + }, [triggerWizard]); // handlers @@ -84,10 +91,10 @@ function ModelsConfiguratorModal(props: { // start button const startButton = React.useMemo(() => { if (showWizard) - return ; + return ; // return ; if (!isMultiServices) - return ; + return ; return undefined; // if (isMultiServices) { // return ( @@ -102,9 +109,13 @@ function ModelsConfiguratorModal(props: { return ( {showWizard ? 'Welcome · Setup' : 'Configure'} AI Models} + title={showWizard ? ( + + Setup AI Models + + ) : <>Configure AI Models} open onClose={optimaActions().closeModels} - darkBottomClose + darkBottomClose={!showWizard} closeText={showWizard ? 'Done' : undefined} animateEnter={!hasLLMs} unfilterBackdrop @@ -117,7 +128,7 @@ function ModelsConfiguratorModal(props: { {!showWizard && } - + {(showWizard || !!activeService) && } {showWizard && } diff --git a/src/modules/llms/models-modal/ModelsServiceSelector.tsx b/src/modules/llms/models-modal/ModelsServiceSelector.tsx index 3fef8377f..d019db949 100644 --- a/src/modules/llms/models-modal/ModelsServiceSelector.tsx +++ b/src/modules/llms/models-modal/ModelsServiceSelector.tsx @@ -7,6 +7,7 @@ import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import type { DModelsService, DModelsServiceId } from '~/common/stores/llms/modelsservice.types'; import { CloseablePopup } from '~/common/components/CloseablePopup'; import { ConfirmationModal } from '~/common/components/modals/ConfirmationModal'; +import { TooltipOutlined } from '~/common/components/TooltipOutlined'; import { llmsStoreActions, llmsStoreState } from '~/common/stores/llms/store-llms'; import { themeZIndexOverMobileDrawer } from '~/common/app.theme'; import { useIsMobile } from '~/common/components/useMatchMedia'; @@ -18,6 +19,10 @@ import { vendorHasBackendCap } from '../vendors/vendor.helpers'; // import { MODELS_WIZARD_OPTION_ID } from '~/modules/llms/models-modal/ModelsModal'; +// configuration +const ENABLE_DELETE_LAST = true; // This will fall the menu back to the 'Quick Setup' mode. was: Release.IsNodeDevBuild; + + /*function locationIcon(vendor?: IModelVendor | null) { if (vendor && vendor.id === 'openai' && vendorHasBackendCap(...)) return ; @@ -62,7 +67,7 @@ export function ModelsServiceSelector(props: { setSelectedServiceId(modelsService.id); }, [setSelectedServiceId]); - const enableDeleteButton = !!props.selectedServiceId && modelsServices.length > 1; + const enableDeleteButton = !!props.selectedServiceId && (ENABLE_DELETE_LAST || modelsServices.length > 1); const handleDeleteService = React.useCallback(async (serviceId: DModelsServiceId, skipConfirmation: boolean) => { // [shift] to delete without confirmation @@ -197,7 +202,7 @@ export function ModelsServiceSelector(props: { onChange={(_event, value) => value && props.setSelectedServiceId(value)} startDecorator={selectedServiceItem?.icon} slotProps={{ - root: { sx: { minWidth: 190 } }, + root: { sx: { minWidth: 180 } }, indicator: { sx: { opacity: 0.5 } }, }} > @@ -215,7 +220,7 @@ export function ModelsServiceSelector(props: { {/**/} - {isMobile ? ( + {(isMobile && !noServices) ? ( @@ -225,12 +230,16 @@ export function ModelsServiceSelector(props: { )} - props.selectedServiceId && handleDeleteService(props.selectedServiceId, event.shiftKey)} - > - - + {enableDeleteButton && ( + + props.selectedServiceId && handleDeleteService(props.selectedServiceId, event.shiftKey)} + > + + + + )} {/* vendors popup, for adding */} diff --git a/src/modules/llms/models-modal/ModelsWizard.tsx b/src/modules/llms/models-modal/ModelsWizard.tsx index 202b78e06..fdeb933be 100644 --- a/src/modules/llms/models-modal/ModelsWizard.tsx +++ b/src/modules/llms/models-modal/ModelsWizard.tsx @@ -23,14 +23,34 @@ const WizardVendors = [ ] as const; -const wizardContainerSx = { - margin: 'calc(-1 * var(--Card-padding, 1rem))', - padding: 'var(--Card-padding)', - // background: 'linear-gradient(135deg, var(--joy-palette-primary-500), var(--joy-palette-primary-700))', - background: 'linear-gradient(135deg, var(--joy-palette-background-level1), var(--joy-palette-background-level1))', - display: 'grid', - gap: 'var(--Card-padding)', -}; +const _styles = { + + container: { + margin: 'calc(-1 * var(--Card-padding, 1rem))', + padding: 'var(--Card-padding)', + // paddingRight: 'calc(1.5 * var(--Card-padding))', + // background: 'linear-gradient(135deg, var(--joy-palette-primary-500), var(--joy-palette-primary-700))', + background: 'linear-gradient(135deg, var(--joy-palette-background-level1), var(--joy-palette-background-level1))', + display: 'grid', + gap: 'calc(0.75 * var(--Card-padding))', + } as const, + + text1: { + my: 1, + ml: 7.25, + display: 'flex', + flexDirection: 'column', + gap: 0.25, + } as const, + + text2: { + my: 1, + ml: 7.25, + color: 'text.tertiary', + fontSize: 'sm', + } as const, + +} as const; function WizardProviderSetup(props: { @@ -202,26 +222,26 @@ export function ModelsWizard(props: { onSwitchToAdvanced?: () => void, }) { return ( - + - - {/**/} - {/* Quick Start*/} - {/**/} - - Enter API keys to connect Big-AGI to your AI providers.{' '} - {/*{!props.isMobile && <>Switch to Advanced for more options.}*/} + + + Enter API keys to connect your AI services. + {/**/} + {/* Enter API keys to connect your AI services.{' '}*/} + {/* {!props.isMobile && <>Switch to Advanced for more options.}*/} + {/**/} {WizardVendors.map(({ vendor, apiKeyField }, index) => ( ))} - + {/*{!props.isMobile && <>Switch to Advanced to choose between {getModelVendorsCount()} services.}{' '}*/} - {!props.isMobile && <>Switch to Advanced for more services.}{' '} - Or skip for now and do it later. + {!props.isMobile && <>Switch to Advanced for more services,}{' '} + or skip for now and do it later. diff --git a/src/modules/llms/server/openai/openai.router.ts b/src/modules/llms/server/openai/openai.router.ts index 977312362..6c543ed4f 100644 --- a/src/modules/llms/server/openai/openai.router.ts +++ b/src/modules/llms/server/openai/openai.router.ts @@ -399,7 +399,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu let oaiHost = fixupHost(access.oaiHost || env.OPENAI_API_HOST || DEFAULT_OPENAI_HOST, apiPath); // warn if no key - only for default (non-overridden) hosts if (!oaiKey && oaiHost.indexOf(DEFAULT_OPENAI_HOST) !== -1) - throw new Error('Missing OpenAI API Key. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new Error('Missing OpenAI API Key. Add it on the UI or server side (your deployment).'); // [Helicone] // We don't change the host (as we do on Anthropic's), as we expect the user to have a custom host. @@ -489,7 +489,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu case 'openpipe': const openPipeKey = access.oaiKey || env.OPENPIPE_API_KEY || ''; if (!openPipeKey) - throw new Error('Missing OpenPipe API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new Error('Missing OpenPipe API Key or Host. Add it on the UI or server side (your deployment).'); return { headers: { @@ -515,7 +515,7 @@ export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | nu } if (!orKey || !orHost) - throw new Error('Missing OpenRouter API Key or Host. Add it on the UI (Models Setup) or server side (your deployment).'); + throw new Error('Missing OpenRouter API Key or Host. Add it on the UI or server side (your deployment).'); return { headers: { diff --git a/src/modules/llms/vendors/ApproximateCosts.tsx b/src/modules/llms/vendors/ApproximateCosts.tsx index ad98c9ba6..b701d7b83 100644 --- a/src/modules/llms/vendors/ApproximateCosts.tsx +++ b/src/modules/llms/vendors/ApproximateCosts.tsx @@ -20,6 +20,7 @@ const _styles = { // style fontSize: 'sm', backgroundColor: 'neutral.softBg', + // boxShadow: 'inset 0px 1px 4px -2px rgba(0, 0, 0, 0.2)', // border borderBottom: '1px solid', diff --git a/src/modules/llms/vendors/anthropic/AnthropicServiceSetup.tsx b/src/modules/llms/vendors/anthropic/AnthropicServiceSetup.tsx index d3748d865..fc367dad5 100644 --- a/src/modules/llms/vendors/anthropic/AnthropicServiceSetup.tsx +++ b/src/modules/llms/vendors/anthropic/AnthropicServiceSetup.tsx @@ -50,7 +50,7 @@ export function AnthropicServiceSetup(props: { serviceId: DModelsServiceId }) { - Enjoy Sonnet 3.5, Opus and Haiku. Anthropic servers status. + Enjoy Sonnet, Opus and Haiku · Anthropic servers status @@ -66,17 +66,6 @@ export function AnthropicServiceSetup(props: { serviceId: DModelsServiceId }) { placeholder='sk-...' /> - - - - {autoVndAntBreakpoints ? 'User & Auto' : 'User-driven'} - - - + + + + + {autoVndAntBreakpoints ? 'User & Auto' : 'User-driven'} + + + {advanced.on && ) ?? null : null; } -export function getDefaultModelVendor(): IModelVendor { - return MODEL_VENDOR_REGISTRY.openai; -} \ No newline at end of file +// export function getDefaultModelVendor(): IModelVendor { +// return MODEL_VENDOR_REGISTRY.openai; +// } \ No newline at end of file