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