mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 14:10:15 -07:00
Add Vendors to Sources (ok). Configure Sources (bad)
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Box, Button, Option, Select, Typography } from '@mui/joy';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import CloudOutlinedIcon from '@mui/icons-material/CloudOutlined';
|
||||
import ComputerIcon from '@mui/icons-material/Computer';
|
||||
|
||||
import { hideOnMobile } from '@/common/theme';
|
||||
|
||||
import { DModelSource, DModelSourceId, ModelVendorId } from './store-models';
|
||||
|
||||
|
||||
interface ModelVendorDescription {
|
||||
id: ModelVendorId;
|
||||
name: string;
|
||||
multiple: boolean;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
const MODEL_VENDOR_DESCRIPTIONS: ModelVendorDescription[] = [
|
||||
{ id: 'openai', name: 'OpenAI', multiple: false, icon: <CloudOutlinedIcon /> },
|
||||
{ id: 'localai', name: 'LocalAI', multiple: true, icon: <ComputerIcon /> },
|
||||
{ id: 'google_vertex', name: 'Google Vertex', multiple: false, icon: <CloudOutlinedIcon /> },
|
||||
{ id: 'azure_openai', name: 'Azure OpenAI', multiple: false, icon: <CloudOutlinedIcon /> },
|
||||
{ id: 'anthropic', name: 'Anthropic', multiple: false, icon: <CloudOutlinedIcon /> },
|
||||
];
|
||||
|
||||
const DEFAULT_MODEL_VENDOR_ID: ModelVendorId = 'openai';
|
||||
|
||||
|
||||
export function AddVendor(props: { llmSources: DModelSource[], onAddSource: (llmSource: DModelSource) => void }) {
|
||||
// state
|
||||
const [selectedVendorId, setSelectedVendorId] = React.useState<ModelVendorId | null>(DEFAULT_MODEL_VENDOR_ID);
|
||||
|
||||
const vendorItems = React.useMemo(() => MODEL_VENDOR_DESCRIPTIONS.map(vendor => {
|
||||
const existingCount = props.llmSources.filter(source => source.vendorId === vendor.id).length;
|
||||
const disabled = !vendor.multiple && existingCount >= 1 || existingCount >= 2;
|
||||
return {
|
||||
vendor,
|
||||
disabled,
|
||||
component: <Option key={vendor.id} value={vendor.id} disabled={disabled}>{vendor.name}</Option>,
|
||||
existingCount,
|
||||
};
|
||||
}), [props.llmSources]);
|
||||
|
||||
const selectedVendor = vendorItems.find(item => item.vendor.id === selectedVendorId);
|
||||
|
||||
const handleAddSource = () => {
|
||||
if (!selectedVendor || selectedVendor.disabled)
|
||||
return;
|
||||
|
||||
// create a unique DModelSourceId
|
||||
const vendorId = selectedVendor.vendor.id;
|
||||
let sourceId: DModelSourceId = vendorId;
|
||||
let suffix = 0;
|
||||
if (selectedVendor.existingCount > 0) {
|
||||
suffix += 2;
|
||||
while (props.llmSources.find(source => source.sourceId === `${sourceId}-${suffix}`))
|
||||
suffix++;
|
||||
sourceId = `${sourceId}-${suffix}`;
|
||||
}
|
||||
|
||||
// add the new configuration
|
||||
props.onAddSource({
|
||||
sourceId,
|
||||
label: selectedVendor.vendor.name + (suffix > 0 ? ` #${suffix}` : ''),
|
||||
vendorId,
|
||||
});
|
||||
};
|
||||
|
||||
// if there are no configs, add the default one
|
||||
React.useEffect(() => {
|
||||
if (!props.llmSources.length) {
|
||||
const vendorId = DEFAULT_MODEL_VENDOR_ID;
|
||||
props.onAddSource({
|
||||
sourceId: vendorId,
|
||||
label: MODEL_VENDOR_DESCRIPTIONS.find(vendor => vendor.id === vendorId)?.name || '',
|
||||
vendorId,
|
||||
});
|
||||
}
|
||||
}, [props, selectedVendorId]);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', gap: 1 }}>
|
||||
|
||||
<Typography sx={{ mr: 1, ...hideOnMobile }}>
|
||||
Source
|
||||
</Typography>
|
||||
|
||||
<Select
|
||||
variant='outlined'
|
||||
value={selectedVendorId}
|
||||
onChange={(event, value) => setSelectedVendorId(value)}
|
||||
startDecorator={selectedVendor?.vendor?.icon}
|
||||
// endDecorator={selectedVendor?.existingCount ? <CheckOutlinedIcon /> : null}
|
||||
slotProps={{
|
||||
root: { sx: { minWidth: 190 } },
|
||||
indicator: { sx: { opacity: 0.5 } },
|
||||
}}
|
||||
>
|
||||
{vendorItems.map(option => option.component)}
|
||||
</Select>
|
||||
|
||||
<Button variant='plain' disabled={!!selectedVendor?.disabled} onClick={handleAddSource} startDecorator={<AddIcon />}>
|
||||
Add
|
||||
</Button>
|
||||
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Chip, IconButton, Radio, RadioGroup } from '@mui/joy';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
||||
|
||||
import { DModelSource, DModelSourceId } from '@/modules/models/store-models';
|
||||
import { LocalAISource } from '@/modules/models/localai/LocalAISource';
|
||||
import { OpenAISource } from '@/modules/models/openai/OpenAISource';
|
||||
|
||||
|
||||
export function ConfigureSources(props: {
|
||||
llmSources: DModelSource[],
|
||||
selectedSourceId: DModelSourceId | null,
|
||||
setSelectedSourceId: (sourceId: DModelSourceId | null) => void,
|
||||
onDeleteSourceId: (sourceId: DModelSourceId) => void,
|
||||
}) {
|
||||
|
||||
const sourceItems = React.useMemo(() => props.llmSources.map(source => {
|
||||
const checked = source.sourceId === props.selectedSourceId;
|
||||
return {
|
||||
source,
|
||||
component: (
|
||||
<Chip
|
||||
key={source.sourceId}
|
||||
variant={checked ? 'soft' : 'plain'}
|
||||
color={checked ? 'primary' : 'neutral'}
|
||||
size='lg'
|
||||
startDecorator={
|
||||
checked && <CheckIcon sx={{ zIndex: 1, pointerEvents: 'none' }} />
|
||||
// configured
|
||||
// ? <CheckIcon sx={{ zIndex: 1, pointerEvents: 'none' }} />
|
||||
// : <CheckBoxOutlineBlankOutlinedIcon sx={{ zIndex: 1, pointerEvents: 'none' }} />
|
||||
}
|
||||
>
|
||||
<Radio
|
||||
variant='outlined'
|
||||
checked={checked}
|
||||
// color={checked ? 'danger' : 'warning'}
|
||||
disableIcon
|
||||
overlay
|
||||
label={source.label}
|
||||
value={source.sourceId}
|
||||
/>
|
||||
</Chip>
|
||||
),
|
||||
};
|
||||
}), [props.llmSources, props.selectedSourceId]);
|
||||
|
||||
const selectedSource = sourceItems.find(item => item.source.sourceId === props.selectedSourceId);
|
||||
|
||||
let vendorConfigComponent: React.JSX.Element | null = null;
|
||||
if (selectedSource) {
|
||||
switch (selectedSource.source.sourceId) {
|
||||
case 'openai':
|
||||
vendorConfigComponent = <OpenAISource />;
|
||||
break;
|
||||
case 'google_vertex':
|
||||
break;
|
||||
case 'anthropic':
|
||||
break;
|
||||
case 'localai':
|
||||
vendorConfigComponent = <LocalAISource />;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const enableDeleteButton = !!props.setSelectedSourceId && sourceItems.length >= 2;
|
||||
|
||||
return <>
|
||||
|
||||
{/* Configuration Items */}
|
||||
<RadioGroup
|
||||
overlay
|
||||
value={props.selectedSourceId}
|
||||
onChange={event => props.setSelectedSourceId(event.target.value)}
|
||||
sx={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', gap: 1 }}
|
||||
>
|
||||
{sourceItems.map(item => item.component)}
|
||||
|
||||
{/* Delete Configuration Button */}
|
||||
<IconButton
|
||||
variant='plain' color='neutral'
|
||||
disabled={!enableDeleteButton}
|
||||
onClick={() => props.onDeleteSourceId(props.selectedSourceId!)}
|
||||
sx={{ ml: 'auto' }}
|
||||
>
|
||||
<DeleteOutlineIcon />
|
||||
</IconButton>
|
||||
</RadioGroup>
|
||||
|
||||
{/* Selected Item Configuration */}
|
||||
{vendorConfigComponent}
|
||||
|
||||
</>;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Box, Typography } from '@mui/joy';
|
||||
import * as React from 'react';
|
||||
|
||||
export function Modeling() {
|
||||
return <>
|
||||
<Box>
|
||||
<Typography>
|
||||
Test
|
||||
</Typography>
|
||||
</Box>
|
||||
</>;
|
||||
}
|
||||
Reference in New Issue
Block a user